home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / adrbklib.c < prev    next >
C/C++ Source or Header  |  1996-03-15  |  145KB  |  5,478 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: adrbklib.c,v 4.139 1996/03/16 09:47:52 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. #include "headers.h"
  43. #include "adrbklib.h"
  44.  
  45. #if defined(DOS) || defined(OS2)
  46. #define    ADRBK_NAME    "addrbook"
  47. #define    TMP_ADRBK_NAME    "addrbook.tmp"
  48. #define    WRITE_MODE    "wb"
  49. #define    READ_MODE    "rb"
  50. #else
  51. #define    ADRBK_NAME    ".addressbook"
  52. #define    TMP_ADRBK_NAME    ".addressbook.temp"
  53. #define    WRITE_MODE    "w"
  54. #define    READ_MODE    "r"
  55. #endif
  56.  
  57.  
  58. #ifndef MAXPATH
  59. #define MAXPATH 1000    /* Longest file path we can deal with */
  60. #endif
  61.  
  62. #define MAXLINE 1000    /* Longest line in addrbook */
  63.  
  64. /*
  65.  * The do-while stuff is so these are statements and can be written with
  66.  * a following ; like a regular statement without worrying about braces and all.
  67.  */
  68. #define SKIP_SPACE(p) do{while(*p && *p == SPACE)p++;}while(0)
  69. #define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
  70. #define RM_END_SPACE(start,end)                                             \
  71.         do{char *_ptr = end;                                            \
  72.            while(--_ptr >= start && *_ptr == SPACE)*_ptr = '\0';}while(0)
  73. #define REPLACE_NEWLINES_WITH_SPACE(p)                                      \
  74.         do{register char *_qq;                                      \
  75.            for(_qq = p; *_qq; _qq++)                                \
  76.                if(*_qq == '\n' || *_qq == '\r')                     \
  77.                *_qq = SPACE;}while(0)
  78. #define NO_UID  ((adrbk_uid_t)0)
  79. #define DEFAULT_HTABLE_SIZE 100
  80. #define MAX_CHARS_IN_HASH 50
  81. #define DELETED "#DELETED-"
  82. #define DELETED_LEN 9
  83.  
  84. /*
  85.  * MSC ver 7.0 and less times are since 1900, everybody else's time so far
  86.  * is since 1970. sheesh.
  87.  */
  88. #if    defined(DOS) && (_MSC_VER == 700)
  89. #define EPOCH_ADJ    ((time_t)((time_t)(70*365 + 18) * (time_t)86400))
  90. #endif
  91.  
  92. extern jmp_buf addrbook_changed_unexpectedly;  /* from addrbook.c */
  93. extern char *trouble_filename;                 /*   ditto         */
  94. static forced_rebuilds = 0;  /* forced rebuild even though mtime looks right */
  95. static trouble_rebuilds = 0; /* all rebuilds caused by goto trouble;         */
  96. #define MAX_FORCED_REBUILDS 3
  97. #define MAX_TROUBLE_REBUILDS 10
  98. static int writing; /* so we can give understandable error message */
  99. static adrbk_cntr_t deleted_elem=NO_NEXT;  /* pretend deleted_elem is deleted */
  100. static AdrBk_Entry *deleted_ae=NULL;
  101. static adrbk_cntr_t insert_before=NO_NEXT; /* pretend inserted_entryref comes */
  102. static EntryRef inserted_entryref;         /* before insert_before            */
  103. static adrbk_cntr_t moved_elem=NO_NEXT;    /* pretend moved_elem is moved to  */
  104. static adrbk_cntr_t move_elem_before;      /* right before move_elem_before   */
  105.  
  106. static char empty[]    = "";
  107.  
  108. jmp_buf jump_over_qsort;
  109.  
  110. /*
  111.  * The addrbook entry cache is stored in a hash table.  Each list in the
  112.  * table has up to CACHE_PER_ER_BUCKET entries in it.  The number of hash
  113.  * buckets is adjusted to make this true.  So, for example, if you set the
  114.  * nominal_max_cache_size to 200 and CACHE_PER_ER_BUCKET to 10, there would
  115.  * be a hash array of 20 lists, each with 10 cache entries stored in it.
  116.  * This gives us a way to have constant lookup time (look through no more
  117.  * than 10 entries) regardless of the size of the cache.
  118.  */
  119. #define CACHE_PER_ER_BUCKET 10
  120.  
  121.  
  122. adrbk_cntr_t ab_hash PROTO((char *, a_c_arg_t));
  123. adrbk_uid_t  ab_uid PROTO((char *));
  124. EntryRef    *adrbk_get_entryref PROTO((AdrBk *, a_c_arg_t, Handling));
  125. int          adrbk_write PROTO((AdrBk *, adrbk_cntr_t *, int));
  126. int          bld_hash_from_ondisk_hash PROTO((AdrBk *));
  127. int          build_ondisk_hash_from_abook PROTO((AdrBk *, char *));
  128. void         clear_entryref_cache PROTO((AdrBk *));
  129. void         clearrefs_in_cached_aes PROTO((AdrBk *));
  130. int          cmp_addr PROTO((const QSType *, const QSType *));
  131. int          cmp_cntr_by_full PROTO((const QSType *, const QSType *));
  132. int          cmp_cntr_by_full_lists_last PROTO((const QSType *,const QSType *));
  133. int          cmp_cntr_by_nick PROTO((const QSType *, const QSType *));
  134. int          cmp_cntr_by_nick_lists_last PROTO((const QSType *,const QSType *));
  135. int          cmp_ae_by_full PROTO((const QSType *, const QSType *));
  136. int          cmp_ae_by_full_lists_last PROTO((const QSType *, const QSType *));
  137. int          cmp_ae_by_nick PROTO((const QSType *, const QSType *));
  138. int          cmp_ae_by_nick_lists_last PROTO((const QSType *, const QSType *));
  139. AdrBk_Entry *copy_ae PROTO((AdrBk_Entry *));
  140. void         exp_add_nth PROTO((EXPANDED_S *, a_c_arg_t));
  141. void         exp_del_nth PROTO((EXPANDED_S *, a_c_arg_t));
  142. void         fix_sort_rule_in_hash PROTO((AdrBk *));
  143. void         free_ab_adrhash PROTO((AdrHash **));
  144. void         free_ab_entryref PROTO((AdrBk *, EntryRef *));
  145. char        *get_entryref_line_from_disk PROTO((FILE *, char *, a_c_arg_t));
  146. int          get_sort_rule_from_disk PROTO((FILE *));
  147. time_t       get_adj_fp_file_mtime PROTO((FILE *));
  148. time_t       get_adj_name_file_mtime PROTO((char *));
  149. time_t       get_adj_time PROTO((void));
  150. time_t       get_timestamp_from_disk PROTO((FILE *));
  151. adrbk_cntr_t hashtable_size PROTO((a_c_arg_t));
  152. void         init_adrhash_array PROTO((AdrHash *, a_c_arg_t));
  153. AdrBk_Entry *init_ae_entry PROTO((AdrBk *, EntryRef *));
  154. void         init_entryref_cache PROTO((AdrBk *));
  155. int          length_of_entry PROTO((FILE *, long));
  156. AdrHash     *new_adrhash PROTO((a_c_arg_t));
  157. EntryRef    *new_entryref PROTO((adrbk_uid_t, adrbk_uid_t, long));
  158. int          ok_to_blast_it PROTO((FILE *));
  159. adrbk_cntr_t re_sort_particular_entryref PROTO((AdrBk *, a_c_arg_t));
  160. void         set_inserted_entryref PROTO((AdrBk *, a_c_arg_t, AdrBk_Entry *));
  161. void         set_moved_entryref PROTO((a_c_arg_t, a_c_arg_t));
  162. char        *skip_to_next_addr PROTO((char *));
  163. char        *skip_to_next_nickname PROTO((FILE *, long *, char **, long *,
  164.                                 int, int *));
  165. void         sort_addr_list PROTO((char **));
  166. int          valid_hfile PROTO((FILE *));
  167. int          write_hash_header PROTO((FILE *, a_c_arg_t));
  168. int          write_hash_table PROTO((AdrHash *, FILE *, a_c_arg_t));
  169. int          write_hash_trailer PROTO((AdrBk *, FILE *, int));
  170. int          write_single_abook_entry PROTO((AdrBk_Entry *, FILE *, int *,
  171.                             int *, int *, int *));
  172. int          write_single_entryref PROTO((EntryRef *, FILE *));
  173.  
  174.  
  175. /*
  176.  *  Open, read, and parse an address book.
  177.  *
  178.  * Args: filename -- the filename to open if specified
  179.  *       homedir  -- the user's home directory if specified
  180.  *       warning  -- put "why failed" message to user here
  181.  *
  182.  * If filename is NULL, the default will be used in the homedir
  183.  * passed in.  If homedir is NULL, the current dir will be used.
  184.  * If filename is not NULL and is an absolute path, just the filename
  185.  * will be used.  Otherwise, it will be used relative to the homedir, or
  186.  * to the current dir depending on whether or not homedir is NULL.
  187.  *
  188.  * Expected addressbook file format is:
  189.  *  <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
  190.  *
  191.  * The last two fields (\t<fcc>\t<comment>) are optional.
  192.  *
  193.  * Lines that start with SPACE are continuation lines.  Ends of lines are
  194.  * treated as if they were spaces.  The address field is either a single
  195.  * address or a list of comma-separated addresses inside parentheses.
  196.  *
  197.  * Fields missing from the end of an entry are considered blank.
  198.  *
  199.  * Commas in the address field will cause problems, as will tabs in any
  200.  * field.
  201.  *
  202.  * There may be some deleted entries in the addressbook that don't get
  203.  * used.  They look like regular entries except their nicknames start
  204.  * with the string "#DELETED-YY/MM/DD#".
  205.  */
  206. AdrBk *
  207. adrbk_open(filename, homedir, warning, sort_rule, just_create_lu, lu_not_valid)
  208.     char *filename,
  209.      *homedir,
  210.      *warning;
  211.     int   sort_rule,
  212.       just_create_lu,  /* for special use, create .lu file and that's it */
  213.       lu_not_valid;
  214. {
  215.     register char *p;
  216.     char           path[MAXPATH];
  217.     AdrBk         *ab;
  218.     int            got_it, create_it;
  219.     int            tried_shortname = 0;
  220.     int            tried_tmpfile = 0;
  221.     int           we_cancel = 0;
  222.  
  223.  
  224.     dprint(2, (debugfile, "- adrbk_open(%s) -\n", filename));
  225.  
  226.     ab        = (AdrBk *)fs_get(sizeof(AdrBk));
  227.     memset(ab, 0, sizeof(AdrBk));
  228.  
  229.     ab->orig_filename = filename ? cpystr(filename) : NULL;
  230.  
  231.     /*------------ figure out and save name of file to open ---------*/
  232.     if(filename == NULL){
  233.         if(homedir != NULL){
  234.         build_path(path, homedir, ADRBK_NAME);
  235.             ab->filename = cpystr(path);
  236.         }
  237.     else
  238.           ab->filename = cpystr(ADRBK_NAME);
  239.     }
  240.     else{
  241.     if(is_absolute_path(filename)){
  242.             ab->filename = cpystr(filename);
  243.         }
  244.     else{
  245.             if(homedir != NULL){
  246.         build_path(path, homedir, filename);
  247.                 ab->filename = cpystr(path);
  248.             }
  249.         else
  250.               ab->filename = cpystr(filename);
  251.         }
  252.     }
  253.  
  254.     if(p = last_cmpnt(ab->filename)){
  255.     strncpy(path, ab->filename, p - ab->filename - 1);
  256.         path[p - ab->filename - 1] = '\0';
  257.     p = path;
  258.     }
  259.     else
  260.       p = ".";
  261.  
  262.     ab->temp_filename = temp_nam(p, "a1");
  263.     ab->temp_hashfile = temp_nam(p, "a2");
  264.     if(!ab->temp_filename || !ab->temp_hashfile){
  265.     if(warning)
  266.       (void)strcpy(warning, "Can't create temporary file");
  267.  
  268.     goto bail_out;
  269.     }
  270.  
  271.     /* open addrbook for reading */
  272.     ab->fp = fopen(ab->filename, READ_MODE);
  273.     if(ab->fp == NULL){
  274.         /*--- No address book, try creating one ----*/
  275.     q_status_message1(SM_INFO, 0, 3,
  276.         "Address book %s doesn't exist, creating",
  277.         last_cmpnt(ab->filename));
  278.     dprint(2, (debugfile, "Address book %s doesn't exist, creating\n",
  279.         ab->filename));
  280.         ab->fp = fopen(ab->filename, "w");
  281.         if(ab->fp == NULL         ||
  282.         fclose(ab->fp) == EOF ||
  283.         (ab->fp = fopen(ab->filename, READ_MODE)) == NULL){
  284.             /*--- Create failed, bail out ---*/
  285.         if(warning)
  286.           (void)strcpy(warning, error_description(errno));
  287.  
  288.         dprint(2, (debugfile, "create failed: %s\n",
  289.         error_description(errno)));
  290.         goto bail_out;
  291.         }
  292.     }
  293.  
  294.     /* record new change date of addrbook file */
  295.     if(just_create_lu)
  296.       ab->last_change_we_know_about = (time_t)(-1);
  297.     else
  298.       ab->last_change_we_know_about = get_adj_fp_file_mtime(ab->fp);
  299.  
  300.     init_entryref_cache(ab);
  301.  
  302.     ab->hashfile = cpystr(strcat(strcpy(path, ab->filename),
  303.     ADRHASH_FILE_SUFFIX));
  304.  
  305. try_again:
  306.     if(can_access(ab->hashfile, ACCESS_EXISTS) == 0){
  307.     if(can_access(ab->hashfile, EDIT_ACCESS) == 0)
  308.       ab->hashfile_access = ReadWrite;
  309.     else if(can_access(ab->hashfile, READ_ACCESS) == 0)
  310.       ab->hashfile_access = ReadOnly;
  311.     else
  312.       ab->hashfile_access = NoAccess;
  313.     }
  314.     else
  315.       ab->hashfile_access = NoExists;
  316.  
  317.     if(ab->hashfile_access == ReadOnly || ab->hashfile_access == NoAccess)
  318.       ab->sort_rule = AB_SORT_RULE_NONE;
  319.     else
  320.       ab->sort_rule = sort_rule;
  321.  
  322.     got_it = 0;
  323.     create_it = 0;
  324.     if(ab->hashfile_access == ReadWrite || ab->hashfile_access == ReadOnly){
  325.     time_t mtime, timestamp;
  326.  
  327.     /* check to see if up-to-date */
  328.     mtime = get_adj_fp_file_mtime(ab->fp);
  329.     if(mtime != (time_t)(-1)){
  330.         ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  331.         if(lu_not_valid)
  332.           dprint(2, (debugfile, "lu forced not valid\n"));
  333.  
  334.         if(!lu_not_valid && valid_hfile(ab->fp_hash)){
  335.             if((timestamp=get_timestamp_from_disk(ab->fp_hash)) >= mtime){
  336.             /* Ok, hashfile is up-to-date, use it */
  337.             we_cancel = busy_alarm(1, NULL, NULL, 0);
  338.             if(bld_hash_from_ondisk_hash(ab) == 0)
  339.               got_it++;
  340.  
  341.             if(we_cancel)
  342.               cancel_busy_alarm(-1);
  343.             }
  344.             else
  345.               dprint(2, (debugfile,
  346.               "lu is out of date: timestamp=%lu file_mtime=%lu\n",
  347.               (unsigned long)timestamp, (unsigned long)mtime));
  348.         }
  349.         else{
  350.         if(!just_create_lu && !ok_to_blast_it(ab->fp_hash)){
  351.             int ans;
  352.             char prompt[500];
  353.  
  354.             if(ab->hashfile_access == ReadWrite){
  355.                 dprint(2, (debugfile, "ask if ok to blast %s\n",
  356.                 ab->hashfile));
  357.             sprintf(prompt,
  358.                 "Pine needs to update lookup file %s, ok",
  359.                 ab->hashfile);
  360.             ans = want_to(prompt, 'y', 'n', NO_HELP, 0, 0);
  361.             if(ans != 'y'){
  362.                 dprint(2, (debugfile,
  363.                 "user says not ok to blast\n"));
  364.                 if(warning && !*warning)
  365.                   (void)strcpy(warning,
  366.                     "Can't create lookup database");
  367.  
  368.                 if(ab->fp_hash){
  369.                 (void)fclose(ab->fp_hash);
  370.                 ab->fp_hash = (FILE *)NULL;
  371.                 }
  372.  
  373.                 goto try_tempfile;
  374.             }
  375.             else
  376.               dprint(2, (debugfile, "user says ok to blast\n"));
  377.             }
  378.         }
  379.         }
  380.  
  381.         if(!got_it && ab->fp_hash){
  382.         (void)fclose(ab->fp_hash);
  383.         ab->fp_hash = (FILE *)NULL;
  384.         }
  385.     }
  386.     }
  387.  
  388.     if(!got_it){
  389.     if(ab->hashfile_access == ReadWrite)
  390.       create_it++;
  391.     /* See if can create it */
  392.     else if(ab->hashfile_access == NoExists){
  393.         FILE *fp;
  394.         
  395. #ifdef    DOS
  396.        {char *dot;
  397.           if((dot = strindex(ab->hashfile, '.')) && strindex(dot, '.')){
  398.           fp = NULL;
  399.           q_status_message1(SM_ORDER, 4, 4,
  400.                "Can't create %s, don't use \".\" in addrbook name",
  401.                last_cmpnt(ab->hashfile));
  402.           display_message('x');
  403.           dprint(2, (debugfile, "Can't create \"%s\", change name of\n  addrbook \"%s\" so that it doesn't have a \".\" in it.\n  Using temp file for now.\n", ab->hashfile, ab->filename));
  404.           errno = 0;
  405.        }
  406.        else{
  407. #endif
  408.         q_status_message1(SM_INFO, 0, 3,
  409.         "Lookup file %s doesn't exist, creating",
  410.         last_cmpnt(ab->hashfile));
  411.         display_message('x');
  412.         dprint(2, (debugfile, "Lookup file %s doesn't exist, creating\n",
  413.         ab->hashfile));
  414.         errno = 0;
  415.         fp = fopen(ab->hashfile, WRITE_MODE);
  416. #ifdef    DOS
  417.       }
  418.     }
  419. #endif
  420.         if(fp == NULL){
  421.         if(errno == ENAMETOOLONG && !tried_shortname){
  422.  
  423.             /*
  424.              * We know that the addressbook name, say "filename", is
  425.              * short enough, and that "filename.lu" is too long.
  426.              * Try a name the same length as "filename".
  427.              * "filename.lu" -> "filen_.l".
  428.              * Note that this may not be the best of ideas.  If more
  429.              * than one address book maps to the same name, we're
  430.              * probably in for some trouble.
  431.              */
  432.              tried_shortname++;
  433.              p    = &(ab->hashfile[strlen(ab->hashfile) - 6]);
  434.              *p++ = '_';
  435.              *p++ = '.';
  436.              *p++ = 'l';
  437.              *p   = '\0';
  438.              q_status_message1(SM_INFO, 0, 3,
  439.              "filename too long, using %s",
  440.              last_cmpnt(ab->hashfile));
  441.              dprint(2, (debugfile, "name too long, trying %s\n",
  442.              ab->hashfile));
  443.              goto try_again;
  444.         }
  445.         else{
  446.             /* have to try /tmp file below */
  447.             if(tried_tmpfile || just_create_lu){
  448.             q_status_message(SM_ORDER, 0, 3,
  449.                 "problems accessing addressbook");
  450.             display_message('x');
  451.             goto bail_out;
  452.             }
  453.         }
  454.         }
  455.         else{
  456.         (void)fclose(fp);
  457.         create_it++;
  458.         }
  459.     }
  460.  
  461.  
  462.     /*
  463.      * If we can't create the hashfile in the right place, put it in
  464.      * a temporary file for the duration of this session.
  465.      */
  466.     if(!create_it && !tried_tmpfile){
  467.         char savename[500];
  468.  
  469. try_tempfile:
  470.         if(just_create_lu)
  471.           goto bail_out;
  472.  
  473.         tried_tmpfile++;
  474.         (void)strcpy(savename, ab->hashfile);
  475.         if(ab->hashfile)
  476.           fs_give((void **)&ab->hashfile);
  477.  
  478.         if(ab->temp_hashfile)
  479.           fs_give((void **)&ab->temp_hashfile);
  480.  
  481.         ab->hashfile = temp_nam(NULL, "a3");
  482.         if(ab->hashfile == NULL)
  483.           goto bail_out;
  484.  
  485.         dprint(2, (debugfile, "trying tmpfile %s\n", ab->hashfile));
  486.         ab->temp_hashfile = temp_nam(NULL, "a4");
  487.         if(ab->temp_hashfile == NULL)
  488.           goto bail_out;
  489.  
  490.         ab->delete_hashfile = 1;
  491.         q_status_message1(SM_ORDER, 3, 3,
  492.         "Can't create %s, using temp file", last_cmpnt(savename));
  493.         goto try_again;
  494.     }
  495.  
  496.     if(create_it){
  497.         dprint(2, (debugfile, "%s is not valid, rebuilding\n",
  498.          ab->hashfile));
  499.         if(ab->hashfile_access != NoExists)
  500.           if(lu_not_valid)
  501.             q_status_message1(SM_INFO, 0, 1, "forcing rebuild of %s...",
  502.             last_cmpnt(ab->hashfile));
  503.           else
  504.             q_status_message1(SM_INFO, 0,1, "%s isn't valid, rebuilding...",
  505.             last_cmpnt(ab->hashfile));
  506.  
  507.         if(!just_create_lu)
  508.           display_message('x');
  509.  
  510.         if(!just_create_lu)
  511.           we_cancel = busy_alarm(2, "still rebuilding", NULL, 0);
  512.  
  513.         if(build_ondisk_hash_from_abook(ab,
  514.                     (warning && !*warning) ? warning : NULL)){
  515.         if(we_cancel)
  516.               cancel_busy_alarm(-1);
  517.  
  518.         dprint(2,
  519.          (debugfile, "failed in build_ondisk_hash_from_abook\n"));
  520.         goto bail_out;  /* Failed */
  521.         }
  522.  
  523.         if(we_cancel)
  524.           cancel_busy_alarm(-1);
  525.     }
  526.     }
  527.  
  528.     if(ab){
  529.     /* allocate header for expanded lists list */
  530.     ab->exp      = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  531.     /* first real element is NULL */
  532.     ab->exp->next = (EXPANDED_S *)NULL;
  533.  
  534.     /* allocate header for checked entries list */
  535.     ab->checks       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  536.     /* first real element is NULL */
  537.     ab->checks->next = (EXPANDED_S *)NULL;
  538.  
  539.     return(ab);
  540.     }
  541.  
  542. bail_out:
  543.     if(ab->fp)
  544.       (void)fclose(ab->fp);
  545.  
  546.     if(ab->fp_hash)
  547.       (void)fclose(ab->fp_hash);
  548.  
  549.     if(ab->hashfile){
  550.     unlink(ab->hashfile);
  551.     fs_give((void **)&ab->hashfile);
  552.     }
  553.  
  554.     if(ab->orig_filename)
  555.       fs_give((void **)&ab->orig_filename);
  556.  
  557.     if(ab->filename)
  558.       fs_give((void **)&ab->filename);
  559.  
  560.     if(ab->temp_filename)
  561.       fs_give((void **)&ab->temp_filename);
  562.  
  563.     if(ab->temp_hashfile)
  564.       fs_give((void **)&ab->temp_hashfile);
  565.  
  566.     fs_give((void **)&ab);
  567.  
  568.     return NULL;
  569. }
  570.  
  571.  
  572. /*
  573.  * Checks whether or not the addrbook is sorted correctly according to
  574.  * the SortType.  Returns 1 if is sorted correctly, 0 otherwise.
  575.  */
  576. int
  577. adrbk_is_in_sort_order(ab, be_quiet)
  578.     AdrBk *ab;
  579.     int    be_quiet;
  580. {
  581.     adrbk_cntr_t entry;
  582.     AdrBk_Entry *ae, *ae_prev;
  583.     int (*cmp_func)();
  584.     int last_time_sorted_rule;
  585.     int we_cancel = 0;
  586.  
  587.     dprint(9, (debugfile, "- adrbk_is_in_sort_order -\n"));
  588.  
  589.     if(!ab)
  590.       return 0;
  591.  
  592.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  593.       return 1;
  594.     
  595.     if(ab->count < 2)
  596.       return 1;
  597.  
  598.     /*
  599.      * If it's the same, we can assume it is in sort order.
  600.      * This is only actually true if the client is playing by the
  601.      * rules.  The rule that matters here is that you have to sort
  602.      * the addrbook before you can do stuff like add to it or delete
  603.      * from it.  Those operations assume it is already sorted and
  604.      * will record that fact in the variable we're checking below.
  605.      * We're ok because addrbook.c always sorts the addrbook when it
  606.      * finds it is out of order.
  607.      */
  608.     last_time_sorted_rule = get_sort_rule_from_disk(ab->fp_hash);
  609.     if(last_time_sorted_rule != -1 && ab->sort_rule == last_time_sorted_rule)
  610.       return 1;
  611.  
  612.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  613.                         cmp_ae_by_full_lists_last :
  614.                (ab->sort_rule == AB_SORT_RULE_FULL) ?
  615.                         cmp_ae_by_full :
  616.                (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  617.                         cmp_ae_by_nick_lists_last :
  618.             /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  619.                         cmp_ae_by_nick;
  620.  
  621.     ae_prev = adrbk_get_ae(ab, (a_c_arg_t)0, Normal);
  622.  
  623.     if(!be_quiet)
  624.       we_cancel = busy_alarm(1, NULL, NULL, 0);
  625.  
  626.     for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t)entry, Normal);
  627.     ae != (AdrBk_Entry *)NULL;
  628.     ae = adrbk_get_ae(ab, (a_c_arg_t)(++entry), Normal)){
  629.  
  630.         if((*cmp_func)((QSType *)&ae_prev, (QSType *)&ae) > 0){
  631.         if(we_cancel)
  632.           cancel_busy_alarm(-1);
  633.  
  634.         return 0;
  635.         }
  636.  
  637.         ae_prev = ae;
  638.     }
  639.  
  640.     /*
  641.      * Do this so that we won't have to go through the whole addrbook
  642.      * to check next time we open it.
  643.      */
  644.     fix_sort_rule_in_hash(ab);
  645.  
  646.     if(we_cancel)
  647.       cancel_busy_alarm(-1);
  648.  
  649.     return 1;
  650. }
  651.  
  652.  
  653. void
  654. fix_sort_rule_in_hash(ab)
  655.     AdrBk *ab;
  656. {
  657.     register FILE *fp_for_old_hash;
  658.     register FILE *fp_for_new_hash;
  659.     register int c;
  660.     long filesize, all_but_sort_rule;
  661.  
  662.     if(!ab || ab->fp_hash == (FILE *)NULL)
  663.       return;
  664.  
  665.     filesize = (SIZEOF_HDR + ab->count * SIZEOF_ENTRYREF_ENTRY +
  666.     2 * ab->htable_size * SIZEOF_HTABLE_ENTRY + SIZEOF_TRLR);
  667.     
  668.     all_but_sort_rule = filesize - SIZEOF_SORT_RULE - SIZEOF_NEWLINE;
  669.  
  670.     if((fp_for_new_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  671.       return;
  672.     
  673.     fp_for_old_hash = ab->fp_hash;
  674.     rewind(fp_for_old_hash);
  675.  
  676.     /*
  677.      * Straight copy of all but the sort rule at the end.
  678.      * Everything else is ok.  The in core stuff is ok, too.  This is
  679.      * because the sort rule in the file is used only for the purpose
  680.      * of checking to see if it's already sorted.
  681.      */
  682.     while(all_but_sort_rule-- > 0L){
  683.     if((c = getc(fp_for_old_hash)) == EOF ||
  684.        putc(c, fp_for_new_hash) == EOF){
  685.  
  686.         /* shouldn't happen */
  687.         (void)fclose(fp_for_new_hash);
  688.         (void)unlink(ab->temp_hashfile);
  689.         return;
  690.     }
  691.     }
  692.  
  693.     /* add the sort rule (the 2 is SIZEOF_SORT_RULE) */
  694.     if(fprintf(fp_for_new_hash, "%2d\n", ab->sort_rule) == EOF){
  695.     (void)fclose(fp_for_new_hash);
  696.     (void)unlink(ab->temp_hashfile);
  697.     return;
  698.     }
  699.  
  700.     if(fclose(fp_for_new_hash) == EOF){
  701.     (void)unlink(ab->temp_hashfile);
  702.     return;
  703.     }
  704.  
  705.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  706.     (void)fclose(ab->fp_hash);
  707.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  708.       (void)unlink(ab->temp_hashfile);
  709.  
  710.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  711. }
  712.  
  713.  
  714. /*
  715.  * Returns 1 if it is ok to overwrite the file.
  716.  */
  717. int
  718. ok_to_blast_it(fp)
  719.     FILE *fp;
  720. {
  721.     char buf[SIZEOF_PMAGIC + 1];
  722.     long filesize;
  723.  
  724.     if(fp == (FILE *)NULL)
  725.       return 0;
  726.  
  727.     /* check if file is empty */
  728.     if((filesize = fp_file_size(fp)) == -1L)
  729.       return 0;
  730.  
  731.     if(filesize == 0L)
  732.       return 1;
  733.  
  734.     /* check for header PMAGIC (or LEGACY_PMAGIC) */
  735.     if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
  736.       return 0;
  737.  
  738.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
  739.       return 0;
  740.  
  741.     buf[SIZEOF_PMAGIC] = '\0';
  742.     if(strcmp(buf, PMAGIC) == 0 || strcmp(buf, LEGACY_PMAGIC) == 0)
  743.       return 1;
  744.  
  745.     return 0;
  746. }
  747.  
  748.  
  749. /*
  750.  * Sanity checks on hashfile.
  751.  * Returns 1 if ok.
  752.  */
  753. int
  754. valid_hfile(fp)
  755.     FILE *fp;
  756. {
  757.     char buf[SIZEOF_ASCII_LONG + 1];
  758.     long hashsize, num_elements, filesize;
  759.  
  760.     dprint(9, (debugfile, "- valid_hfile -\n"));
  761.  
  762.     if(fp == (FILE *)NULL)
  763.       return 0;
  764.  
  765.     /* check for header PMAGIC */
  766.     if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0)){
  767.     dprint(2, (debugfile, "lu not valid - can't seek to PMAGIC\n"));
  768.     return 0;
  769.     }
  770.  
  771.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC){
  772.     dprint(2, (debugfile, "lu not valid - can't read PMAGIC\n"));
  773.     return 0;
  774.     }
  775.  
  776.     buf[SIZEOF_PMAGIC] = '\0';
  777.     if(strcmp(buf, PMAGIC) != 0){
  778.     dprint(2, (debugfile, "lu not valid - PMAGIC is %s\n", buf));
  779.     return 0;
  780.     }
  781.  
  782.     /* check for matching version number */
  783.     if(fseek(fp, (long)TO_FIND_VERSION_NUM, 0)){
  784.     dprint(2, (debugfile, "lu not valid - can't seek to VERS_NUM\n"));
  785.     return 0;
  786.     }
  787.  
  788.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
  789.     SIZEOF_VERSION_NUM){
  790.     dprint(2, (debugfile, "lu not valid - can't read VERS_NUM\n"));
  791.     return 0;
  792.     }
  793.  
  794.     buf[SIZEOF_VERSION_NUM] = '\0';
  795.     if(strcmp(buf, ADRHASH_FILE_VERSION_NUM) != 0){
  796.     dprint(2, (debugfile, "lu not valid - VERS_NUM is %s not %s\n",
  797.         buf, ADRHASH_FILE_VERSION_NUM));
  798.     return 0;
  799.     }
  800.  
  801.     /* check for reasonable hashtable size */
  802.     if(fseek(fp, (long)TO_FIND_HTABLE_SIZE, 0)){
  803.     dprint(2, (debugfile, "lu not valid - can't seek to HTABLE_SIZE\n"));
  804.     return 0;
  805.     }
  806.  
  807.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_HASH_SIZE, fp) !=
  808.                             SIZEOF_HASH_SIZE){
  809.     dprint(2, (debugfile, "lu not valid - can't read HTABLE_SIZE\n"));
  810.     return 0;
  811.     }
  812.  
  813.     buf[SIZEOF_HASH_SIZE] = '\0';
  814.     hashsize = atol(buf);
  815.     if(hashsize <= 10L || hashsize > MAX_HASHTABLE_SIZE){
  816.     dprint(2, (debugfile, "lu not valid - hashsize is %s\n", buf));
  817.     return 0;
  818.     }
  819.  
  820.     /* check for trailer PMAGIC */
  821.     if(fseek(fp, (long)TO_FIND_TRLR_PMAGIC, 2)){
  822.     dprint(2, (debugfile, "lu not valid - can't seek to TRL_PMAGIC\n"));
  823.     return 0;
  824.     }
  825.  
  826.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC){
  827.     dprint(2, (debugfile, "lu not valid - can't read TRL_PMAGIC\n"));
  828.     return 0;
  829.     }
  830.  
  831.     buf[SIZEOF_PMAGIC] = '\0';
  832.     if(strcmp(buf, PMAGIC) != 0){
  833.     dprint(2, (debugfile, "lu not valid - TRL_PMAGIC is %s\n", buf));
  834.     return 0;
  835.     }
  836.  
  837.     /* check for reasonable number of entries */
  838.     if(fseek(fp, (long)TO_FIND_COUNT, 2)){
  839.     dprint(2, (debugfile, "lu not valid - can't seek to COUNT\n"));
  840.     return 0;
  841.     }
  842.  
  843.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_COUNT, fp) != SIZEOF_COUNT){
  844.     dprint(2, (debugfile, "lu not valid - can't read COUNT\n"));
  845.     return 0;
  846.     }
  847.  
  848.     buf[SIZEOF_COUNT] = '\0';
  849.     num_elements = atol(buf);
  850.     if(num_elements < 0L || num_elements > MAX_ADRBK_SIZE){
  851.     dprint(2, (debugfile, "lu not valid - COUNT is %s\n", buf));
  852.     return 0;
  853.     }
  854.  
  855.     /* check size of file */
  856.     if((filesize = fp_file_size(fp)) == -1L){
  857.     dprint(2, (debugfile, "lu not valid - fp_file_size failed\n"));
  858.     return 0;
  859.     }
  860.  
  861.     if(filesize != (SIZEOF_HDR + num_elements * SIZEOF_ENTRYREF_ENTRY +
  862.     2 * hashsize * SIZEOF_HTABLE_ENTRY + SIZEOF_TRLR)){
  863.     dprint(2, (debugfile, "lu not valid - filesize is %ld\n", filesize));
  864.     return 0;
  865.     }
  866.  
  867.     return 1;
  868. }
  869.  
  870.  
  871. int
  872. bld_hash_from_ondisk_hash(ab)
  873.     AdrBk *ab;
  874. {
  875.     long               cnt;
  876.     char               buf[SIZEOF_HASH_SIZE + 1];
  877.     char              *p;
  878.     register char     *q;
  879.     long               nick_hash_offset;
  880.     size_t             adrhashtable_size;
  881.     adrbk_cntr_t       i;
  882.     size_t             psize;
  883.     adrbk_cntr_t      *array;
  884.     WIDTH_INFO_S      *widths;
  885.  
  886.     dprint(9, (debugfile, "- bld_hash_from_ondisk_hash -\n"));
  887.  
  888.     if(!ab || !ab->fp_hash)
  889.       return -1;
  890.  
  891.     /* get htable size */
  892.     if(fseek(ab->fp_hash, (long)TO_FIND_HTABLE_SIZE, 0) == 0 &&
  893.        fread(buf, sizeof(char), (unsigned)SIZEOF_HASH_SIZE, ab->fp_hash) ==
  894.                             SIZEOF_HASH_SIZE){
  895.     buf[SIZEOF_HASH_SIZE] = '\0';
  896.     ab->htable_size = atoi(buf);
  897.     }
  898.     else
  899.       return -1;
  900.  
  901.     adrhashtable_size = ab->htable_size * SIZEOF_HTABLE_ENTRY;
  902.  
  903.     psize = max(2 * adrhashtable_size, SIZEOF_TRLR);
  904.     p     = (char *)fs_get(psize);
  905.  
  906.     if(fseek(ab->fp_hash, (long)TO_FIND_TRLR_PMAGIC, 2))
  907.       return -1;
  908.  
  909.     if(fread(p, sizeof(char), (unsigned)SIZEOF_TRLR, ab->fp_hash) !=
  910.                                 SIZEOF_TRLR)
  911.       return -1;
  912.  
  913.     q = p + SIZEOF_PMAGIC + SIZEOF_SPACE;
  914.     cnt = atol(q);
  915.     if(cnt < 0L)
  916.       return -1;
  917.     else
  918.       ab->count = (adrbk_cntr_t)cnt;
  919.  
  920.     q += (SIZEOF_COUNT + SIZEOF_NEWLINE);
  921.     ab->deleted_cnt = atol(q);
  922.  
  923.     q += (SIZEOF_DELETED_CNT + SIZEOF_NEWLINE + SIZEOF_SPACE);
  924.     widths = &ab->widths;
  925.     widths->max_nickname_width = atoi(q);
  926.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  927.     widths->max_fullname_width = atoi(q);
  928.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  929.     widths->max_addrfield_width = atoi(q);
  930.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  931.     widths->max_fccfield_width = atoi(q);
  932.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  933.     widths->third_biggest_fullname_width = atoi(q);
  934.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  935.     widths->third_biggest_addrfield_width = atoi(q);
  936.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  937.     widths->third_biggest_fccfield_width = atoi(q);
  938.  
  939.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  940.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  941.  
  942.     nick_hash_offset = SIZEOF_HDR + ab->count * SIZEOF_ENTRYREF_ENTRY;
  943.  
  944.     if(fseek(ab->fp_hash, nick_hash_offset, 0) == 0 &&
  945.        fread(p, sizeof(char), (2 * adrhashtable_size), ab->fp_hash) ==
  946.                            2 * adrhashtable_size){
  947.  
  948.     dprint(9, (debugfile, "initializing hash_by_nick\n"));
  949.     /* initialize hash_by_nick array */
  950.     array = ab->hash_by_nick->harray;
  951.     q = p;
  952.     for(i = 0; i < ab->htable_size; i++){
  953.         array[i] = (adrbk_cntr_t)strtoul(q, (char **)NULL, 10);
  954.         q += SIZEOF_HTABLE_ENTRY;
  955.     }
  956.  
  957.     dprint(9, (debugfile, "initializing hash_by_addr\n"));
  958.     /* initialize hash_by_addr array */
  959.     array = ab->hash_by_addr->harray;
  960.     for(i = 0; i < ab->htable_size; i++){
  961.         array[i] = (adrbk_cntr_t)strtoul(q, (char **)NULL, 10);
  962.         q += SIZEOF_HTABLE_ENTRY;
  963.     }
  964.     }
  965.     else{
  966.     free_ab_adrhash(&ab->hash_by_nick);
  967.     free_ab_adrhash(&ab->hash_by_addr);
  968.     fs_give((void **)&p);
  969.     return -1;
  970.     }
  971.  
  972.     fs_give((void **)&p);
  973.  
  974.     return 0;
  975. }
  976.  
  977.  
  978. char *
  979. get_entryref_line_from_disk(fp, buf, entry_num)
  980.     FILE     *fp;
  981.     char      buf[];
  982.     a_c_arg_t entry_num;
  983. {
  984.     long seek_position;
  985.     size_t rv;
  986.  
  987.     if(!fp){
  988.     dprint(2, (debugfile, "get_entryref_line_from_disk returning NULL!\n"));
  989.     dprint(2, (debugfile, "    fp was NULL\n"));
  990.     return NULL;
  991.     }
  992.  
  993.     seek_position = SIZEOF_HDR + (long)entry_num * SIZEOF_ENTRYREF_ENTRY;
  994.  
  995.     if(fseek(fp, seek_position, 0)){
  996.     dprint(2, (debugfile, "get_entryref_line_from_disk returning NULL!\n"));
  997.     dprint(2, (debugfile,
  998.             "    fseek failed, seek_position=%ld, entry_num=%lu, %s\n",
  999.             seek_position, (unsigned long)entry_num,
  1000.             error_description(errno)));
  1001.     return NULL;
  1002.     }
  1003.  
  1004.     errno = 0;
  1005.     clearerr(fp);
  1006.     if((rv=fread(buf, sizeof(char), (unsigned)SIZEOF_ENTRYREF_ENTRY, fp)) !=
  1007.     SIZEOF_ENTRYREF_ENTRY){
  1008.     int saverrno = errno;
  1009.  
  1010.     dprint(2, (debugfile,
  1011.         "get_entryref_line_from_disk returning NULL!\n"));
  1012.     dprint(2, (debugfile,
  1013.         "    fread returned %ld instead of %d, %s (errno=%d)\n",
  1014.         (long)rv, SIZEOF_ENTRYREF_ENTRY, error_description(saverrno),
  1015.         saverrno));
  1016.     dprint(2, (debugfile, "    seek_position=%ld, entry_num=%lu\n",
  1017.         seek_position, (unsigned long)entry_num));
  1018.     if(rv == 0)
  1019.       dprint(2, (debugfile, "    ferror(fp)=%d, feof(fp)=%d\n",
  1020.         ferror(fp), feof(fp)));
  1021.  
  1022.     return NULL;
  1023.     }
  1024.  
  1025.     buf[SIZEOF_ENTRYREF_ENTRY] = '\0';
  1026.  
  1027.     return(buf);
  1028. }
  1029.  
  1030.  
  1031. time_t
  1032. get_timestamp_from_disk(fp)
  1033.     FILE *fp;
  1034. {
  1035.     char buf[SIZEOF_TIMESTAMP + 1];
  1036.  
  1037.     dprint(9, (debugfile, "- get_timestamp_from_disk -\n"));
  1038.  
  1039.     if(fp == (FILE *)NULL)
  1040.       return (time_t)0;
  1041.  
  1042.     if(fseek(fp, (long)TO_FIND_TIMESTAMP, 2))
  1043.       return (time_t)0;
  1044.  
  1045.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_TIMESTAMP, fp) !=
  1046.                                 SIZEOF_TIMESTAMP)
  1047.       return (time_t)0;
  1048.  
  1049.     buf[SIZEOF_TIMESTAMP] = '\0';
  1050.     return((time_t)strtoul(buf, (char **)NULL, 10));
  1051. }
  1052.  
  1053.  
  1054. /*
  1055.  * Adjust the mtime to return time since Unix epoch.  DOS is off by 70 years.
  1056.  */
  1057. time_t
  1058. get_adj_fp_file_mtime(fp)
  1059.     FILE *fp;
  1060. {
  1061.     time_t mtime;
  1062.  
  1063.     mtime = fp_file_mtime(fp);
  1064.  
  1065. #ifdef EPOCH_ADJ
  1066.     if(mtime != (time_t)(-1))
  1067.       mtime -= EPOCH_ADJ;
  1068. #endif
  1069.  
  1070.     return(mtime);
  1071. }
  1072.  
  1073. time_t
  1074. get_adj_name_file_mtime(name)
  1075.     char *name;
  1076. {
  1077.     time_t mtime;
  1078.  
  1079.     mtime = name_file_mtime(name);
  1080.  
  1081. #ifdef EPOCH_ADJ
  1082.     if(mtime != (time_t)(-1))
  1083.       mtime -= EPOCH_ADJ;
  1084. #endif
  1085.  
  1086.     return(mtime);
  1087. }
  1088.  
  1089. time_t
  1090. get_adj_time()
  1091. {
  1092.     time_t tt;
  1093.  
  1094.     tt = time((time_t *)0);
  1095.  
  1096. #ifdef EPOCH_ADJ
  1097.     tt -= EPOCH_ADJ;
  1098. #endif
  1099.  
  1100.     return(tt);
  1101. }
  1102.  
  1103.  
  1104. int
  1105. get_sort_rule_from_disk(fp)
  1106.     FILE *fp;
  1107. {
  1108.     char buf[SIZEOF_SORT_RULE + 1];
  1109.  
  1110.     dprint(9, (debugfile, "- get_sort_rule_from_disk -\n"));
  1111.  
  1112.     if(!fp)
  1113.       return -1;
  1114.  
  1115.     if(fseek(fp, (long)TO_FIND_SORT_RULE, 2))
  1116.       return -1;
  1117.  
  1118.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_SORT_RULE, fp) !=
  1119.                                 SIZEOF_SORT_RULE)
  1120.       return -1;
  1121.  
  1122.     buf[SIZEOF_SORT_RULE] = '\0';
  1123.     return(atoi(buf));
  1124. }
  1125.  
  1126.  
  1127. /*
  1128.  * Builds the ondisk (and incore) hash file from the ondisk address book.
  1129.  * This only happens if the hash file is missing or corrupt.
  1130.  */
  1131. int
  1132. build_ondisk_hash_from_abook(ab, warning)
  1133.     AdrBk *ab;
  1134.     char *warning;
  1135. {
  1136.     FILE          *fp_for_hash, *fp_in;
  1137.     EntryRef       e;
  1138.     char          *nickname;
  1139.     char          *address;
  1140.     adrbk_cntr_t   used;
  1141.     adrbk_cntr_t   hash;
  1142.     long           offset;
  1143.     int            max_nick = 0,
  1144.            max_addr = 0, addr_two = 0, addr_three = 0,
  1145.            this_nick_width, this_addr_width;
  1146.     int           longline = 0, rew = 1;
  1147.     WIDTH_INFO_S  *widths;
  1148.     unsigned long  filesize;
  1149.  
  1150.     dprint(9, (debugfile, "- build_ondisk_hash_from_abook -\n"));
  1151.  
  1152.     if(!ab || !ab->hashfile || !ab->temp_hashfile || !ab->fp)
  1153.       return -1;
  1154.  
  1155.     errno = 0;
  1156.  
  1157.     if((fp_for_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  1158.       return -1;
  1159.  
  1160.     fp_in = ab->fp;
  1161.  
  1162.     /* get size of file to estimate good hashtable_size */
  1163.     if((filesize = (unsigned long)fp_file_size(fp_in)) == (unsigned long)-1L)
  1164.       ab->htable_size  = DEFAULT_HTABLE_SIZE;
  1165.     else{
  1166.     a_c_arg_t     approx_number_of_entries;
  1167.  
  1168.     approx_number_of_entries = (a_c_arg_t)(filesize / 50);
  1169.     ab->htable_size  = hashtable_size(approx_number_of_entries);
  1170.     }
  1171.  
  1172.     ab->deleted_cnt  = 0L;  /* number #DELETED- */
  1173.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  1174.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  1175.  
  1176.     if(write_hash_header(fp_for_hash, (a_c_arg_t)ab->htable_size))
  1177.       goto io_err;
  1178.  
  1179.     used = 0;
  1180.  
  1181.     while((nickname =
  1182.       skip_to_next_nickname(fp_in,&offset,&address,NULL,
  1183.                 rew,&longline)) != NULL){
  1184.  
  1185.     rew = 0;
  1186.  
  1187.     if(strncmp(nickname, DELETED, DELETED_LEN) == 0
  1188.        && isdigit(nickname[DELETED_LEN])
  1189.        && isdigit(nickname[DELETED_LEN+1])
  1190.        && nickname[DELETED_LEN+2] == '/'
  1191.        && isdigit(nickname[DELETED_LEN+3])
  1192.        && isdigit(nickname[DELETED_LEN+4])
  1193.        && nickname[DELETED_LEN+5] == '/'
  1194.        && isdigit(nickname[DELETED_LEN+6])
  1195.        && isdigit(nickname[DELETED_LEN+7])
  1196.        && nickname[DELETED_LEN+8] == '#'){
  1197.         ab->deleted_cnt++;
  1198.         continue;
  1199.     }
  1200.  
  1201.     ALARM_BLIP();
  1202.     if((long)used > MAX_ADRBK_SIZE){
  1203.         q_status_message2(SM_ORDER | SM_DING, 4, 5,
  1204.                 "Max addrbook size is %s, %s too large, giving up",
  1205.                 long2string(MAX_ADRBK_SIZE),
  1206.                 last_cmpnt(ab->filename));
  1207.         goto io_err;
  1208.     }
  1209.  
  1210.     e.uid_nick  = ab_uid(nickname);
  1211.     this_nick_width = strlen(nickname);
  1212.     e.offset    = offset;
  1213.     e.ae        = (AdrBk_Entry *)NULL;
  1214.     hash        = ab_hash(nickname, (a_c_arg_t)ab->htable_size);
  1215.     e.next_nick = ab->hash_by_nick->harray[hash];
  1216.     ab->hash_by_nick->harray[hash] = used;
  1217.     if(address && *address != '('){ /* not a list */
  1218.         e.uid_addr  = ab_uid(address);
  1219.         this_addr_width = strlen(address);
  1220.         hash        = ab_hash(address, (a_c_arg_t)ab->htable_size);
  1221.         e.next_addr = ab->hash_by_addr->harray[hash];
  1222.         ab->hash_by_addr->harray[hash] = used;
  1223.     }
  1224.     else{
  1225.         if(address && *address)
  1226.           this_addr_width = strlen(address) - 2;
  1227.         else
  1228.           this_addr_width = 0;
  1229.  
  1230.         e.uid_addr  = NO_UID;
  1231.         e.next_addr = NO_NEXT;
  1232.     }
  1233.  
  1234.     used++;
  1235.     if(write_single_entryref(&e, fp_for_hash))
  1236.       goto io_err;
  1237.  
  1238.     /*
  1239.      * Keep track of widths.  These are only approximate.  If we ever
  1240.      * do an adrbk_write we'll get the exact numbers.  We don't have
  1241.      * any idea of fullname widths so we'll just use the same as the
  1242.      * addrfield widths to get the drawing off the ground.  Same for
  1243.      * the fcc widths.
  1244.      */
  1245.     max_nick = max(max_nick, this_nick_width);
  1246.     if(this_addr_width > max_addr){
  1247.         addr_three = addr_two;
  1248.         addr_two   = max_addr;
  1249.         max_addr   = this_addr_width;
  1250.     }
  1251.     else if(this_addr_width > addr_two){
  1252.         addr_three = addr_two;
  1253.         addr_two   = this_addr_width;
  1254.     }
  1255.     else if(this_addr_width > addr_three){
  1256.         addr_three = this_addr_width;
  1257.     }
  1258.     }
  1259.  
  1260.     if(longline){
  1261.     if(warning)
  1262.       (void)strcpy(warning, "line too long: must be fixed by hand");
  1263.  
  1264.     if(ab->fp_hash){
  1265.         (void)fclose(ab->fp_hash);
  1266.         ab->fp_hash = (FILE *)NULL;
  1267.     }
  1268.  
  1269.     free_ab_adrhash(&ab->hash_by_nick);
  1270.     free_ab_adrhash(&ab->hash_by_addr);
  1271.  
  1272.     return -1;
  1273.     }
  1274.     
  1275.     ab->count = used;
  1276.  
  1277.     widths = &ab->widths;
  1278.     widths->max_nickname_width  = max_nick;
  1279.     widths->max_addrfield_width = max_addr;
  1280.     widths->third_biggest_addrfield_width = addr_three;
  1281.     widths->max_fullname_width  = max_addr;
  1282.     widths->max_fccfield_width = max_addr;
  1283.     widths->third_biggest_fullname_width  = addr_three;
  1284.     widths->third_biggest_fccfield_width = addr_three;
  1285.  
  1286.     if(write_hash_table(ab->hash_by_nick, fp_for_hash,
  1287.     (a_c_arg_t)ab->htable_size))
  1288.       goto io_err;
  1289.  
  1290.     if(write_hash_table(ab->hash_by_addr, fp_for_hash,
  1291.     (a_c_arg_t)ab->htable_size))
  1292.       goto io_err;
  1293.  
  1294.     if(write_hash_trailer(ab, fp_for_hash, 0))
  1295.       goto io_err;
  1296.  
  1297.     if(fclose(fp_for_hash) == EOF)
  1298.       goto io_err;
  1299.  
  1300.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  1301.     if(ab->fp_hash){
  1302.     (void)fclose(ab->fp_hash);
  1303.     ab->fp_hash = NULL;        /* in case of problems */
  1304.     }
  1305.  
  1306.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  1307.       goto io_err;
  1308.  
  1309.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  1310.  
  1311.     return 0;
  1312.  
  1313. io_err:
  1314.     if(warning && errno != 0)
  1315.       (void)strcpy(warning, error_description(errno));
  1316.  
  1317.     if(ab->fp_hash){
  1318.     (void)fclose(ab->fp_hash);
  1319.     ab->fp_hash = (FILE *)NULL;
  1320.     }
  1321.  
  1322.     free_ab_adrhash(&ab->hash_by_nick);
  1323.     free_ab_adrhash(&ab->hash_by_addr);
  1324.  
  1325.     return -1;
  1326. }
  1327.  
  1328.  
  1329. static char space[] = " ";
  1330.  
  1331. /*
  1332.  * Returns next nickname, or NULL
  1333.  *
  1334.  * The offset arg is the offset of the nickname in the file, returned to caller.
  1335.  * The address arg is a pointer to the address, returned to caller.
  1336.  * The length arg is returned to caller.  It is length of entire entry.
  1337.  * If rew is set, rewind the file and start over at beginning.
  1338.  * Longline is returned equal to 1 if an input line is too long.
  1339.  */
  1340. char *
  1341. skip_to_next_nickname(fp, offset, address, length, rew, longline)
  1342.     FILE  *fp;
  1343.     long  *offset;
  1344.     char **address;
  1345.     long  *length;
  1346.     int    rew;
  1347.     int   *longline;
  1348. {
  1349.     static char line[MAXLINE+1];
  1350.     static char next_nickname[MAX_NICKNAME+1];
  1351.     static char this_nickname[MAX_NICKNAME+1];
  1352.     static char this_address[MAX_CHARS_IN_HASH+1];
  1353.     static char next_address[MAX_CHARS_IN_HASH+1];
  1354.     char *p;
  1355.     int   c;
  1356.     char *nickname;
  1357.     char *addr;
  1358.     static long next_nickname_offset = 0L;
  1359.     int ok_so_far = 0;
  1360.  
  1361.  
  1362.     if(address)
  1363.       *address = this_address;
  1364.  
  1365.     if(rew){
  1366.     rewind(fp);
  1367.     /* skip leading (bogus) continuation lines */
  1368.     do{
  1369.         *offset  = ftell(fp);
  1370.         line[MAXLINE-1] = '\0';
  1371.         p       = fgets(line, MAXLINE+1, fp);
  1372.         if(p == NULL)
  1373.           return NULL;
  1374.         else if(!(p[MAXLINE-1] == '\0'
  1375.            || p[MAXLINE-1] == '\n'
  1376.            || p[MAXLINE-1] == '\r')){
  1377.         for(c = 0; c < max(ps_global->ttyo->screen_cols - 30, 0); c++)
  1378.           if(p[c] == TAB)
  1379.             p[c] = SPACE;
  1380.         p[c] = '\0';
  1381.         q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1382.                    "Addrbook line too long:  %s...", p);
  1383.         dprint(2, (debugfile,
  1384.             "line too long in build_ondisk_hash_from_abook(1): %s...\n",
  1385.             p));
  1386.         if(longline)
  1387.           *longline = 1;
  1388.  
  1389.         return NULL;
  1390.         }
  1391.     }while(*p == SPACE);
  1392.  
  1393.     nickname = p;
  1394.     SKIP_TO_TAB(p);
  1395.     /* This *should* be true. */
  1396.     if(*p == TAB)
  1397.       ok_so_far++;
  1398.  
  1399.     *p = '\0';
  1400.     /*
  1401.      * We want nickname of "" to be treated as an empty nickname, but
  1402.      * not to end the addrbook.
  1403.      */
  1404.     if(!*nickname)
  1405.       nickname = space;
  1406.  
  1407.     strncpy(next_nickname, nickname, MAX_NICKNAME);
  1408.     next_nickname[MAX_NICKNAME] = '\0';
  1409.     next_nickname_offset = *offset;
  1410.     /* locate address field */
  1411.     if(!ok_so_far) /* no tab after nickname */
  1412.       goto no_address_initially;
  1413.  
  1414.     p++;
  1415.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1416.         c = getc(fp);
  1417.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1418.           c = getc(fp);
  1419.  
  1420.         if(c != SPACE)
  1421.           ok_so_far = 0;
  1422.  
  1423.         (void)ungetc(c, fp);
  1424.         if(ok_so_far){
  1425.         line[MAXLINE-1] = '\0';
  1426.         p = fgets(line, MAXLINE+1, fp);
  1427.         if(!(p == NULL
  1428.            || p[MAXLINE-1] == '\0'
  1429.            || p[MAXLINE-1] == '\n'
  1430.            || p[MAXLINE-1] == '\r')){
  1431.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1432.               if(p[c] == TAB)
  1433.             p[c] = SPACE;
  1434.             p[c] = '\0';
  1435.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1436.                    "Addrbook line too long:  %s...", p);
  1437.             dprint(2, (debugfile,
  1438.             "line too long in build_ondisk_hash_from_abook(2): %s...\n",
  1439.             p));
  1440.             if(longline)
  1441.               *longline = 1;
  1442.  
  1443.             return NULL;
  1444.         }
  1445.         }
  1446.  
  1447.         if(!ok_so_far || p == NULL){
  1448.         ok_so_far = 0;
  1449.         goto no_address_initially;
  1450.         }
  1451.     }
  1452.  
  1453.     /* skip fullname field */
  1454.     SKIP_TO_TAB(p);
  1455.     if(*p != TAB){
  1456.         ok_so_far = 0;
  1457.         goto no_address_initially;
  1458.     }
  1459.  
  1460.     p++;
  1461.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1462.         c = getc(fp);
  1463.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1464.           c = getc(fp);
  1465.  
  1466.         if(c != SPACE)
  1467.           ok_so_far = 0;
  1468.  
  1469.         (void)ungetc(c, fp);
  1470.         if(ok_so_far){
  1471.         line[MAXLINE-1] = '\0';
  1472.         p = fgets(line, MAXLINE+1, fp);
  1473.         if(!(p == NULL
  1474.            || p[MAXLINE-1] == '\0'
  1475.            || p[MAXLINE-1] == '\n'
  1476.            || p[MAXLINE-1] == '\r')){
  1477.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1478.               if(p[c] == TAB)
  1479.             p[c] = SPACE;
  1480.             p[c] = '\0';
  1481.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1482.                    "Addrbook line too long:  %s...", p);
  1483.             dprint(2, (debugfile,
  1484.             "line too long in build_ondisk_hash_from_abook(3): %s...\n",
  1485.             p));
  1486.             if(longline)
  1487.               *longline = 1;
  1488.  
  1489.             return NULL;
  1490.         }
  1491.         }
  1492.  
  1493.         if(!ok_so_far || p == NULL){
  1494.         ok_so_far = 0;
  1495.             goto no_address_initially;
  1496.         }
  1497.     }
  1498.  
  1499.     SKIP_SPACE(p);
  1500.  
  1501. no_address_initially:
  1502.  
  1503.     if(ok_so_far){
  1504.         addr = p;
  1505.         SKIP_TO_TAB(p);
  1506.         *p = '\0';
  1507.         strncpy(next_address, addr, MAX_CHARS_IN_HASH);
  1508.     }
  1509.     else
  1510.       next_address[0] = '\0';  /* won't happen with good input data */
  1511.     }
  1512.  
  1513.     if(next_nickname[0] == '\0')
  1514.       return NULL;
  1515.  
  1516.     strcpy(this_nickname, next_nickname);
  1517.     *offset = next_nickname_offset;
  1518.     strcpy(this_address, next_address);
  1519.  
  1520.     /* skip continuation lines */
  1521.     do{
  1522.     next_nickname_offset = ftell(fp);
  1523.     line[MAXLINE-1] = '\0';
  1524.     p = fgets(line, MAXLINE+1, fp);
  1525.     if(!(p == NULL
  1526.        || p[MAXLINE-1] == '\0'
  1527.        || p[MAXLINE-1] == '\n'
  1528.        || p[MAXLINE-1] == '\r')){
  1529.         for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1530.           if(p[c] == TAB)
  1531.         p[c] = SPACE;
  1532.         p[c] = '\0';
  1533.         q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1534.                "Addrbook line too long:  %s...", p);
  1535.         dprint(2, (debugfile,
  1536.         "line too long in build_ondisk_hash_from_abook(4): %s...\n",p));
  1537.         if(longline)
  1538.           *longline = 1;
  1539.  
  1540.         return NULL;
  1541.     }
  1542.     }while(p && *p == SPACE);
  1543.  
  1544.     if(p){
  1545.     nickname = p;
  1546.     SKIP_TO_TAB(p);
  1547.     if(*p == TAB)  /* this should always happen */
  1548.       ok_so_far++;
  1549.     else
  1550.       ok_so_far = 0;
  1551.  
  1552.     *p = '\0';
  1553.     /*
  1554.      * We want nickname of "" to be treated as an empty nickname, but
  1555.      * not to end the addrbook.
  1556.      */
  1557.     if(!*nickname)
  1558.       nickname = space;
  1559.  
  1560.     strncpy(next_nickname, nickname, MAX_NICKNAME);
  1561.     /* locate address field */
  1562.     if(!ok_so_far) /* no tab after nickname */
  1563.       goto no_address;
  1564.  
  1565.     p++;
  1566.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1567.         c = getc(fp);
  1568.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1569.           c = getc(fp);
  1570.  
  1571.         if(c != SPACE)
  1572.           ok_so_far = 0;
  1573.  
  1574.         (void)ungetc(c, fp);
  1575.         if(ok_so_far){
  1576.         line[MAXLINE-1] = '\0';
  1577.         p = fgets(line, MAXLINE+1, fp);
  1578.         if(!(p == NULL
  1579.            || p[MAXLINE-1] == '\0'
  1580.            || p[MAXLINE-1] == '\n'
  1581.            || p[MAXLINE-1] == '\r')){
  1582.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1583.               if(p[c] == TAB)
  1584.             p[c] = SPACE;
  1585.             p[c] = '\0';
  1586.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1587.                    "Addrbook line too long:  %s...", p);
  1588.             dprint(2, (debugfile,
  1589.             "line too long in build_ondisk_hash_from_abook(5): %s...\n",
  1590.             p));
  1591.             if(longline)
  1592.               *longline = 1;
  1593.  
  1594.             return NULL;
  1595.         }
  1596.         }
  1597.  
  1598.         if(!ok_so_far || p == NULL){
  1599.         ok_so_far = 0;
  1600.         goto no_address;
  1601.         }
  1602.     }
  1603.  
  1604.     /* skip fullname field */
  1605.     SKIP_TO_TAB(p);
  1606.     if(*p != TAB){
  1607.         ok_so_far = 0;
  1608.         goto no_address;
  1609.     }
  1610.  
  1611.     p++;
  1612.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1613.         c = getc(fp);
  1614.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1615.           c = getc(fp);
  1616.  
  1617.         if(c != SPACE)
  1618.           ok_so_far = 0;
  1619.  
  1620.         (void)ungetc(c, fp);
  1621.         if(ok_so_far){
  1622.         line[MAXLINE-1] = '\0';
  1623.         p = fgets(line, MAXLINE+1, fp);
  1624.         if(!(p == NULL
  1625.            || p[MAXLINE-1] == '\0'
  1626.            || p[MAXLINE-1] == '\n'
  1627.            || p[MAXLINE-1] == '\r')){
  1628.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1629.               if(p[c] == TAB)
  1630.             p[c] = SPACE;
  1631.             p[c] = '\0';
  1632.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1633.                    "Addrbook line too long:  %s...", p);
  1634.             dprint(2, (debugfile,
  1635.             "line too long in build_ondisk_hash_from_abook(6): %s...\n",
  1636.             p));
  1637.             if(longline)
  1638.               *longline = 1;
  1639.  
  1640.             return NULL;
  1641.         }
  1642.         }
  1643.  
  1644.         if(!ok_so_far || p == NULL){
  1645.         ok_so_far = 0;
  1646.             goto no_address;
  1647.         }
  1648.     }
  1649.  
  1650.     SKIP_SPACE(p);
  1651.  
  1652. no_address:
  1653.  
  1654.     if(ok_so_far){
  1655.         addr = p;
  1656.         SKIP_TO_TAB(p);
  1657.         *p = '\0';
  1658.         strncpy(next_address, addr, MAX_CHARS_IN_HASH);
  1659.     }
  1660.     else
  1661.       next_address[0] = '\0';  /* shouldn't happen */
  1662.     }
  1663.     else
  1664.       next_nickname[0] = '\0';
  1665.  
  1666.     if(length)
  1667.       *length = next_nickname_offset - *offset;
  1668.  
  1669.     return(this_nickname);
  1670. }
  1671.  
  1672.  
  1673. EntryRef *
  1674. new_entryref(uid_nickname, uid_address, offset)
  1675.     adrbk_uid_t uid_nickname;
  1676.     adrbk_uid_t uid_address;
  1677.     long        offset;
  1678. {
  1679.     EntryRef *e;
  1680.  
  1681.     e            =  (EntryRef *)fs_get(sizeof(EntryRef));
  1682.     e->uid_nick  =  uid_nickname;
  1683.     e->uid_addr  =  uid_address;
  1684.     e->offset    =  offset;
  1685.     e->next_nick =  NO_NEXT;
  1686.     e->next_addr =  NO_NEXT;
  1687.     e->ae        =  (AdrBk_Entry *)NULL;
  1688.  
  1689.     return(e);
  1690. }
  1691.  
  1692.  
  1693. /*
  1694.  * Returns the hash value of name, which will be in the range 0 ... size-1.
  1695.  * This is a standard hash function that should be as evenly distributed
  1696.  * as possible.  I haven't done much research to try to find a good one.
  1697.  * Most important is the distribution of hashing of all the nicknames in
  1698.  * big addrbooks.  Hashing of all the Single addresses is also important.
  1699.  */
  1700. adrbk_cntr_t
  1701. ab_hash(name, size)
  1702.     char     *name;
  1703.     a_c_arg_t size;
  1704. {
  1705.     register unsigned long h = 0L;
  1706.     register unsigned long c;
  1707.     int at_most_this_many_chars_in_hash = MAX_CHARS_IN_HASH;
  1708.     int four_counter = 0;
  1709.     int two_counter = 1;
  1710.  
  1711.     if(!name)
  1712.       return(NO_NEXT);
  1713.  
  1714.     /* Make the hash case independent */
  1715.     while((c = *name++) && at_most_this_many_chars_in_hash-- > 0){
  1716.     if(isspace((int)c))
  1717.       continue;  /* so we don't have to worry about trimming spaces */
  1718.  
  1719.     if(isupper((int)c))
  1720.       c = tolower((int)c);
  1721.  
  1722.     switch(four_counter){
  1723.       case 0:
  1724.         h += (two_counter ? (c << 24) : (c << 25));
  1725.         break;
  1726.       case 1:
  1727.         h += (two_counter ? (c << 16) : (c << 17));
  1728.         break;
  1729.       case 2:
  1730.         h += (two_counter ? (c << 8) : (c << 9));
  1731.         break;
  1732.       case 3:
  1733.         h += (two_counter ? c : (c << 1));
  1734.         break;
  1735.     }
  1736.     four_counter = (four_counter + 1) % 4;
  1737.     if(four_counter == 0)
  1738.       two_counter = (two_counter + 1) % 2;
  1739.     }
  1740.     
  1741.     return(h % (adrbk_cntr_t)size);
  1742. }
  1743.  
  1744.  
  1745. AdrHash *
  1746. new_adrhash(size)
  1747.     a_c_arg_t size;
  1748. {
  1749.     AdrHash *a;
  1750.  
  1751.     a = (AdrHash *)fs_get(sizeof(AdrHash));
  1752.     a->harray = (adrbk_cntr_t *)fs_get((size_t)size * sizeof(adrbk_cntr_t));
  1753.  
  1754.     /*
  1755.      * The ff initialization causes the next_nick and next_addr pointers to
  1756.      * be set to NO_NEXT.
  1757.      */
  1758.     memset(a->harray, 0xff, (size_t)size * sizeof(adrbk_cntr_t));
  1759.  
  1760.     return(a);
  1761. }
  1762.  
  1763.  
  1764. void
  1765. init_adrhash_array(a, size)
  1766.     AdrHash     *a;
  1767.     a_c_arg_t    size;
  1768. {
  1769.     dprint(9, (debugfile, "- init_adrhash_array -\n"));
  1770.  
  1771.     /*
  1772.      * The ff initialization causes the next_nick and next_addr pointers to
  1773.      * be set to NO_NEXT.
  1774.      */
  1775.     memset(a->harray, 0xff, (size_t)size * sizeof(adrbk_cntr_t));
  1776. }
  1777.  
  1778.  
  1779. void
  1780. free_ab_adrhash(a)
  1781.     AdrHash **a;
  1782. {
  1783.     if(!(*a))
  1784.       return;
  1785.  
  1786.     if((*a)->harray)
  1787.       fs_give((void **)&((*a)->harray));
  1788.  
  1789.     fs_give((void **)a);
  1790. }
  1791.  
  1792.  
  1793. /*
  1794.  * Returns a value which is probably unique for name.  That is, if name1 and
  1795.  * name2 are not the same, then uid(name1) probably not equal to uid(name2).
  1796.  * Actually, they only have to be unique within a given hash bucket.  That
  1797.  * is, we don't want both ab_uid(name1) == ab_uid(name2) and
  1798.  *                       ab_hash(name1) == ab_hash(name2).
  1799.  *
  1800.  * Uid should not be NO_UID so we can tell when it hasn't been initialized.
  1801.  */
  1802. adrbk_uid_t
  1803. ab_uid(name)
  1804.     char *name;
  1805. {
  1806.     register adrbk_uid_t u = (adrbk_uid_t)0;
  1807.     int at_most_this_many_chars_in_uid = MAX_CHARS_IN_HASH;
  1808.     int c;
  1809.  
  1810.     /* Make the uid case independent and only depend on first N chars */
  1811.     while((c = *name++) && at_most_this_many_chars_in_uid-- > 0){
  1812.     if(isspace(c))
  1813.       continue;  /* so we don't have to worry about trimming spaces */
  1814.  
  1815.     if(isupper(c))
  1816.       c = tolower(c);
  1817.  
  1818.     /* this comes from emacs, I think */
  1819.     u = ((((u << 4) & 0xffffffff) + (u >> 24)) & 0x0fffffff) + c;
  1820.     }
  1821.     
  1822.     if(u == NO_UID)
  1823.       u++;
  1824.  
  1825.     return(u);
  1826. }
  1827.  
  1828.  
  1829. /*
  1830.  * Given an EntryRef, return the AdrBk_Entry that it points to.  It may
  1831.  * already be cached.
  1832.  */
  1833. AdrBk_Entry *
  1834. init_ae_entry(ab, entry)
  1835.     AdrBk    *ab;
  1836.     EntryRef *entry;
  1837. {
  1838.     char *p;
  1839.     char *buf; /* read entry in here */
  1840.     int   ret, length;
  1841.     int   first_entry = 0;
  1842.     char *addrfield = (char *)NULL;
  1843.     char *addrfield_end;
  1844.     AdrBk_Entry *a = (AdrBk_Entry *)NULL;
  1845.     char  p_msg[800];
  1846.     char *nickname, *fullname, *fcc, *extra;
  1847.     long  offset_of_prev_char;
  1848.     time_t mtime;
  1849.  
  1850.     if(!entry) /* shouldn't ever happen */
  1851.       return(a);
  1852.  
  1853.     /* already cached earlier */
  1854.     if(entry->ae)
  1855.       return(entry->ae);
  1856.  
  1857.     a = adrbk_newentry();
  1858.     entry->ae = a;
  1859.  
  1860.     if(!ab){
  1861.     dprint(2, (debugfile, "init_ae_entry: found trouble: ab is NULL\n"));
  1862.     goto trouble;
  1863.     }
  1864.  
  1865.     length = length_of_entry(ab->fp, entry->offset);
  1866.     if(length <= 0){
  1867.     dprint(2, (debugfile, "init_ae_entry: found trouble: length=%d\n",
  1868.         length));
  1869.     goto trouble;
  1870.     }
  1871.  
  1872.     offset_of_prev_char = entry->offset;
  1873.     if(offset_of_prev_char > 0L){
  1874.     offset_of_prev_char--;
  1875.     length++;
  1876.     }
  1877.     else
  1878.       first_entry++;
  1879.  
  1880.     if(fseek(ab->fp, offset_of_prev_char, 0)){
  1881.     dprint(2, (debugfile,
  1882.         "init_ae_entry: found trouble: fseek to %ld failed\n",
  1883.         offset_of_prev_char));
  1884.     goto trouble;
  1885.     }
  1886.  
  1887.     /* now pointing at the entry (or one before the entry if not first) */
  1888.     buf = (char *)fs_get(length * sizeof(char) + 1);
  1889.     ret = fread(buf, sizeof(char), (unsigned)length, ab->fp);
  1890.     if(ret != length){
  1891.     dprint(2, (debugfile,
  1892.         "init_ae_entry: found trouble: fread returned %d instead of %d\n",
  1893.         ret, length));
  1894.     goto trouble;
  1895.     }
  1896.  
  1897.     buf[length] = '\0';
  1898.     /*
  1899.      * Check to see if things look ok at this offset.
  1900.      */
  1901.     p = buf;
  1902.     if(!first_entry){
  1903.     if(!(*p == '\n' || *p == '\r')){
  1904.         dprint(2, (debugfile,
  1905.            "init_ae_entry: trouble: char before nick at %ld not CR or NL\n",
  1906.         entry->offset));
  1907.         dprint(2, (debugfile, "             : buf = >%s<\n", buf));
  1908.         goto trouble;
  1909.     }
  1910.  
  1911.     p++;
  1912.     }
  1913.  
  1914.     /* done checking for trouble */
  1915.  
  1916.     REPLACE_NEWLINES_WITH_SPACE(p);
  1917.  
  1918.     nickname = p;
  1919.     SKIP_TO_TAB(p);
  1920.     if(!*p){
  1921.     RM_END_SPACE(nickname, p);
  1922.     a->nickname = cpystr(nickname);
  1923.     }
  1924.     else{
  1925.     *p = '\0';
  1926.     RM_END_SPACE(nickname, p);
  1927.     a->nickname = cpystr(nickname);
  1928.     p++;
  1929.     SKIP_SPACE(p);
  1930.     fullname = p;
  1931.     SKIP_TO_TAB(p);
  1932.     if(!*p){
  1933.         RM_END_SPACE(fullname, p);
  1934.         a->fullname = cpystr(fullname);
  1935.     }
  1936.     else{
  1937.         *p = '\0';
  1938.         RM_END_SPACE(fullname, p);
  1939.         a->fullname = cpystr(fullname);
  1940.         p++;
  1941.         SKIP_SPACE(p);
  1942.         addrfield = p;
  1943.         SKIP_TO_TAB(p);
  1944.         if(!*p){
  1945.         RM_END_SPACE(addrfield, p);
  1946.         }
  1947.         else{
  1948.         *p = '\0';
  1949.         RM_END_SPACE(addrfield, p);
  1950.         p++;
  1951.         SKIP_SPACE(p);
  1952.         fcc = p;
  1953.         SKIP_TO_TAB(p);
  1954.         if(!*p){
  1955.             RM_END_SPACE(fcc, p);
  1956.             a->fcc = cpystr(fcc);
  1957.         }
  1958.         else{
  1959.             *p = '\0';
  1960.             RM_END_SPACE(fcc, p);
  1961.             a->fcc = cpystr(fcc);
  1962.             p++;
  1963.             SKIP_SPACE(p);
  1964.             extra = p;
  1965.             p = extra + strlen(extra);
  1966.             RM_END_SPACE(extra, p);
  1967.             a->extra = cpystr(extra);
  1968.         }
  1969.         }
  1970.     }
  1971.     }
  1972.  
  1973.     /* parse addrfield */
  1974.     if(addrfield){
  1975.     if(*addrfield == '('){  /* it's a list */
  1976.         a->tag = List;
  1977.         p = addrfield;
  1978.         addrfield_end = p + strlen(p);
  1979.  
  1980.         /*
  1981.          * Get rid of the parens.
  1982.          * If this isn't true the input file is messed up.
  1983.          */
  1984.         if(p[strlen(p)-1] == ')'){
  1985.         p[strlen(p)-1] = '\0';
  1986.         p++;
  1987.         a->addr.list = parse_addrlist(p);
  1988.         }
  1989.         else{
  1990.         /* put back what was there to start with */
  1991.         *addrfield_end = ')';
  1992.         a->addr.list = (char **)fs_get(sizeof(char *) * 2);
  1993.         a->addr.list[0] = cpystr(addrfield);
  1994.         a->addr.list[1] = NULL;
  1995.         /* just report first error */
  1996.         if(!*p_msg)
  1997.           sprintf(p_msg, "nickname %s: %.500s",
  1998.                 a->nickname, "addressbook entry is corrupt");
  1999.  
  2000.         dprint(1,
  2001.             (debugfile, "parsing error reading addressbook: %s %s\n",
  2002.                    "missing right paren", addrfield));
  2003.         }
  2004.     }
  2005.     else{  /* A plain, single address */
  2006.  
  2007.         a->addr.addr = cpystr(addrfield);
  2008.         a->tag       = Single;
  2009.     }
  2010.     }
  2011.     else{
  2012.     /*
  2013.      * If no addrfield, assume an empty Single.
  2014.      */
  2015.     a->addr.addr = cpystr("");
  2016.     a->tag       = Single;
  2017.     }
  2018.  
  2019.     fs_give((void **)&buf);
  2020.  
  2021.     return(a);
  2022.  
  2023. trouble:
  2024.     /*
  2025.      * Some other process must have changed the on-disk addrbook out from
  2026.      * under us.  Either that, or the hash file must be messed up.  We need
  2027.      * to close down the files and re-open them, else our pointers will
  2028.      * point at crazy places.
  2029.      *
  2030.      * Attempt to verify the change by stat'ing the files.  If mtime didn't
  2031.      * change we'll hope for the best instead of restarting.  We don't want
  2032.      * to get into a restart loop when the file really isn't changing
  2033.      * but the trouble is being triggered for some other reason.
  2034.      *
  2035.      * However, we do want to try to rebuild at least once even if the mtime
  2036.      * looks ok, because somebody may have copied a valid looking .lu file
  2037.      * onto our .lu file (one with the same number of entries).  So we have
  2038.      * this special ad hoc counter (forced_rebuilds) that lets us try to
  2039.      * rebuild a few times regardless of the mtimes.  We also have the
  2040.      * safety net (trouble_rebuilds) to stop us eventually if we get looping
  2041.      * somehow.
  2042.      */
  2043.     dprint(1, (debugfile,
  2044.       "\n\n ADDR    ::: the addressbook file %s and its lookup file %s\n",
  2045.       (ab && ab->filename) ? ab->filename : "?",
  2046.       (ab && ab->hashfile) ? ab->hashfile : "?"));
  2047.     dprint(1, (debugfile,
  2048.       " BOOK    ::: are not consistent with one another.  The lookup\n"));
  2049.     dprint(1, (debugfile,
  2050.       " TROUBLE ::: file may have to be removed and rebuilt.\n"));
  2051.     dprint(1, (debugfile,
  2052.      "         ::: Usually it will fix itself, but if it doesn't, or if it\n"));
  2053.     dprint(1, (debugfile,
  2054.       "         ::: is building temporary lookup files for each user,\n"));
  2055.     dprint(1, (debugfile,
  2056.       "         ::: the sys admin should rebuild it (%s).\n\n",
  2057.       (ab && ab->hashfile) ? ab->hashfile : "?"));
  2058.     if(((ab && ab->last_change_we_know_about != (time_t)(-1) &&
  2059.      (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
  2060.      ab->last_change_we_know_about != mtime) ||
  2061.     forced_rebuilds < MAX_FORCED_REBUILDS) &&
  2062.          trouble_rebuilds < MAX_TROUBLE_REBUILDS){
  2063.  
  2064.     q_status_message(SM_ORDER | SM_DING, 5, 5,
  2065.        "Addrbook has been changed by another process, need to re-sync...");
  2066.     if(writing){
  2067.        writing = 0;
  2068.        q_status_message(SM_ORDER, 3, 5,
  2069.            "Aborting our change to avoid damage...");
  2070.     }
  2071.  
  2072.     dprint(1, (debugfile,
  2073.         "addrbook %s changed while we had it open, longjmp\n",
  2074.         (ab && ab->filename) ? ab->filename : "?"));
  2075.         if(ab && ab->last_change_we_know_about == (time_t)(-1) ||
  2076.        mtime == (time_t)(-1) ||
  2077.        ab->last_change_we_know_about == mtime)
  2078.         forced_rebuilds++;
  2079.  
  2080.     trouble_rebuilds++;
  2081.     /* jump back to a safe place */
  2082.     trouble_filename = (ab && ab->orig_filename)
  2083.                 ? cpystr(ab->orig_filename)
  2084.                 : cpystr("");
  2085.     longjmp(addrbook_changed_unexpectedly, 1);
  2086.     /*NOTREACHED*/
  2087.     }
  2088.  
  2089.     dprint(1, (debugfile,
  2090.     "addrbook trouble (%s), but we're returning null nickname\n",
  2091.     (ab && ab->filename) ? ab->filename : "?"));
  2092.     dprint(1, (debugfile,
  2093.     "Advised user to remove %s and restart pine\n",
  2094.     (ab && ab->hashfile) ? ab->hashfile : "?"));
  2095.     dprint(1, (debugfile,
  2096.     "If %s not owned by user, sys admin may have to rebuild it\n",
  2097.     (ab && ab->hashfile) ? ab->hashfile : "?"));
  2098.     if(trouble_rebuilds++ < MAX_TROUBLE_REBUILDS + 3)
  2099.       q_status_message1(SM_ORDER, 3, 10,
  2100.     "Lookup file %s inconsistent...remove it and restart Pine",
  2101.     (ab && ab->hashfile) ? last_cmpnt(ab->hashfile) : "?");
  2102.  
  2103.     fs_give((void **)&a);
  2104.     entry->ae = (AdrBk_Entry *)NULL;
  2105.     return((AdrBk_Entry *)NULL);
  2106. }
  2107.  
  2108.  
  2109. /*
  2110.  * Parses a string of comma-separated addresses or nicknames into an
  2111.  * array.
  2112.  *
  2113.  * Returns an allocated, null-terminated list, or NULL.
  2114.  */
  2115. char **
  2116. parse_addrlist(addrfield)
  2117.     char *addrfield;
  2118. {
  2119. #define LISTCHUNK  500   /* Alloc this many addresses for list at a time */
  2120.     char **al, **ad;
  2121.     char *next_addr, *cur_addr, *p, *q;
  2122.     int slots = LISTCHUNK;
  2123.  
  2124.     if(!addrfield)
  2125.       return((char **)NULL);
  2126.  
  2127.     /* allocate first chunk */
  2128.     slots = LISTCHUNK;
  2129.     al    = (char **)fs_get(sizeof(char *) * (slots+1));
  2130.     ad    = al;
  2131.  
  2132.     p = addrfield;
  2133.  
  2134.     /* skip any leading whitespace */
  2135.     for(q = p; *q && *q == SPACE; q++)
  2136.       ;/* do nothing */
  2137.  
  2138.     next_addr = (*q) ? q : NULL;
  2139.  
  2140.     /* Loop adding each address in list to array al */
  2141.     for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
  2142.  
  2143.     next_addr = skip_to_next_addr(cur_addr);
  2144.  
  2145.     q = cur_addr;
  2146.     SKIP_SPACE(q);
  2147.  
  2148.     /* allocate more space */
  2149.     if((ad-al) >= slots){
  2150.         slots += LISTCHUNK;
  2151.         fs_resize((void **)&al, sizeof(char *) * (slots+1));
  2152.         ad = al + slots - LISTCHUNK;
  2153.     }
  2154.  
  2155.     *ad++ = cpystr(q);
  2156.     }
  2157.  
  2158.     *ad++ = NULL;
  2159.  
  2160.     /* free up any excess we've allocated */
  2161.     fs_resize((void **)&al, sizeof(char *) * (ad - al));
  2162.     return(al);
  2163. }
  2164.  
  2165.  
  2166. /*
  2167.  * returns length of the address book entry starting at offset
  2168.  */
  2169. int
  2170. length_of_entry(fp, offset)
  2171.     FILE *fp;
  2172.     long  offset;
  2173. {
  2174.     char line[MAXLINE+1];
  2175.     char *p;
  2176.     long new_offset = offset;
  2177.  
  2178.     errno = 0;
  2179.     line[0] = '\0';
  2180.     if(!fp)
  2181.       return -1;
  2182.  
  2183.     if(fseek(fp, offset, 0))
  2184.       return -1;
  2185.  
  2186.     clearerr(fp);
  2187.     errno = 0;
  2188.     p = fgets(line, MAXLINE+1, fp);
  2189.  
  2190.     do{
  2191.     new_offset = ftell(fp);
  2192.     p          = fgets(line, MAXLINE+1, fp);
  2193.     
  2194.     }while(p && *p == SPACE);
  2195.     
  2196.     if(new_offset <= offset){
  2197.     dprint(2,(debugfile,"length_of_entry: trouble: return length=%ld, %s\n",
  2198.         new_offset-offset, error_description(errno)));
  2199.     dprint(2, (debugfile, "    offset=%ld, p=%s\n", offset,
  2200.         p ? (*p ? p : "<empty>") : "<null>"));
  2201.     dprint(2, (debugfile, "    line=%s\n", *line ? line : "<empty>"));
  2202.     if(p == NULL)
  2203.       dprint(2, (debugfile, "    ferror(fp)=%d, feof(fp)=%d\n",
  2204.         ferror(fp), feof(fp)));
  2205.     }
  2206.  
  2207.     return((int)(new_offset - offset));
  2208. }
  2209.  
  2210.  
  2211. /*
  2212.  * Args  cur -- pointer to the start of the current addr in list.
  2213.  *
  2214.  * Returns a pointer to the start of the next addr or NULL if there are
  2215.  * no more addrs.
  2216.  *
  2217.  * Side effect: current addr has trailing white space removed
  2218.  * and is null terminated.
  2219.  */
  2220. char *
  2221. skip_to_next_addr(cur)
  2222.     char *cur;
  2223. {
  2224.     register char *p,
  2225.           *q;
  2226.     char          *ret_pointer;
  2227.     int in_quotes  = 0,
  2228.         in_comment = 0;
  2229.     char prev_char = '\0';
  2230.  
  2231.     /*
  2232.      * Find delimiting comma or end.
  2233.      * Quoted commas and commented commas don't count.
  2234.      */
  2235.     for(q = cur; *q; q++){
  2236.     switch(*q){
  2237.       case COMMA:
  2238.         if(!in_quotes && !in_comment)
  2239.           goto found_comma;
  2240.         break;
  2241.  
  2242.       case LPAREN:
  2243.         if(!in_quotes && !in_comment)
  2244.           in_comment = 1;
  2245.         break;
  2246.  
  2247.       case RPAREN:
  2248.         if(in_comment && prev_char != BSLASH)
  2249.           in_comment = 0;
  2250.         break;
  2251.  
  2252.       case QUOTE:
  2253.         if(in_quotes && prev_char != BSLASH)
  2254.           in_quotes = 0;
  2255.         else if(!in_quotes && !in_comment)
  2256.           in_quotes = 1;
  2257.         break;
  2258.  
  2259.       default:
  2260.         break;
  2261.     }
  2262.  
  2263.     prev_char = *q;
  2264.     }
  2265.     
  2266. found_comma:
  2267.     if(*q){  /* trailing comma case */
  2268.     *q = '\0';
  2269.     ret_pointer = q + 1;
  2270.     }
  2271.     else
  2272.       ret_pointer = NULL;  /* no more addrs after cur */
  2273.  
  2274.     /* remove trailing white space from cur */
  2275.     for(p = q - 1; p >= cur && isspace(*p); p--)
  2276.       *p = '\0';
  2277.     
  2278.     return(ret_pointer);
  2279. }
  2280.  
  2281.  
  2282. /*
  2283.  * Return the size of the address book 
  2284.  */
  2285. adrbk_cntr_t
  2286. adrbk_count(ab)
  2287.     AdrBk *ab;
  2288. {
  2289.     return(ab ? ab->count : (adrbk_cntr_t)0);
  2290. }
  2291.  
  2292.  
  2293. /*
  2294.  * Get the ae that has index number "entry_num" in "handling" mode.
  2295.  *
  2296.  * Handling - Normal - Means it may be deleted from cache out from under us.
  2297.  *            Lock   - Means it may not be deleted from the cache (so that
  2298.  *                     we can continue to use pointers to it) until we
  2299.  *                     Unlock it explicitly or until adrbk_write is called.
  2300.  *            Unlock - Usually just used to unlock a locked entry.  Adrbk_write
  2301.  *                     also does this unlocking.  It also returns the ae.
  2302.  *   Returns NULL if entry_num is out of range.  Otherwise, it returns an ae.
  2303.  *   It never returns NULL when it should return an ae.  Instead, if it can't
  2304.  *   figure out what the entry is, it returns an empty, Single entry.  This
  2305.  *   means that the users of adrbk_get_ae don't have to check for NULL.
  2306.  *   Note, however, that it is possible that a caller will get back an empty
  2307.  *   Single while expecting a List.
  2308.  */
  2309. AdrBk_Entry *
  2310. adrbk_get_ae(ab, entry_num, handling)
  2311.     AdrBk    *ab;
  2312.     a_c_arg_t entry_num;
  2313.     Handling  handling;
  2314. {
  2315.     EntryRef *entry = (EntryRef *)NULL;
  2316.     AdrBk_Entry *ae = (AdrBk_Entry *)NULL;
  2317.  
  2318.     dprint(9, (debugfile, "- adrbk_get_ae -\n"));
  2319.  
  2320.     if(ab && entry_num >= (a_c_arg_t)ab->count)
  2321.       return(ae);
  2322.  
  2323.     entry = adrbk_get_entryref(ab, entry_num, handling);
  2324.     if(entry != NULL && entry->uid_nick != NO_UID)
  2325.       ae = init_ae_entry(ab, entry);
  2326.  
  2327. #ifdef DEBUG
  2328.     if(ae == (AdrBk_Entry *)NULL){
  2329.     dprint(2, (debugfile, "adrbk_get_ae (%s): returning NULL!\n",
  2330.         ab->filename));
  2331.     dprint(2, (debugfile, "   : count %ld l_c_w_k_a %ld cur_time %lu\n",
  2332.         (long)ab->count, (long)ab->last_change_we_know_about,
  2333.         (unsigned long)get_adj_time()));
  2334.     dprint(2, (debugfile, "   : requested entry_num %ld\n",
  2335.         (long)entry_num));
  2336.     if(entry == NULL){
  2337.         dprint(2,
  2338.          (debugfile, "   : got back NULL entry from adrbk_get_entryref\n"));
  2339.     }
  2340.     else{
  2341.         dprint(2, (debugfile, "   : uid_nick %ld uid_addr %ld offset\n",
  2342.         (long)entry->uid_nick, (long)entry->uid_addr, entry->offset));
  2343.     }
  2344.     }
  2345. #endif /* DEBUG */
  2346.  
  2347.     /* This assigns a non-null value to ae. */
  2348.     if(ae == (AdrBk_Entry *)NULL){
  2349.     ae = adrbk_newentry();
  2350.     ae->tag       = Single;
  2351.     ae->nickname  = cpystr("");
  2352.     ae->addr.addr = cpystr("");
  2353.     if(entry)
  2354.       entry->ae = ae;
  2355.     /* else, memory leak that shouldn't happen often */
  2356.     }
  2357.  
  2358.     return(ae);
  2359. }
  2360.  
  2361.  
  2362. /*
  2363.  * Look up an entry in the address book given a nickname
  2364.  *
  2365.  * Args: ab       -- the address book
  2366.  *       nickname -- nickname to match
  2367.  *      entry_num -- if matched, return entry_num of match here
  2368.  *
  2369.  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
  2370.  *
  2371.  * Lookups usually need to be recursive in case the address
  2372.  * book references itself.  This is left to the next level up.
  2373.  * adrbk_clearrefs() is provided to clear all the reference tags in
  2374.  * the address book for loop detetction.
  2375.  * When there are duplicates of the same nickname we return the first.
  2376.  * This can only happen if addrbook was edited externally.
  2377.  */
  2378. AdrBk_Entry *
  2379. adrbk_lookup_by_nick(ab, nickname, entry_num)
  2380.     AdrBk *ab;
  2381.     char  *nickname;
  2382.     adrbk_cntr_t *entry_num;
  2383. {
  2384.     adrbk_cntr_t hash;
  2385.     adrbk_uid_t uid;
  2386.     adrbk_cntr_t ind, last_ind;
  2387.     EntryRef *entry, *last_one;
  2388.     AdrBk_Entry *ae;
  2389.  
  2390.     dprint(2, (debugfile, "- adrbk_lookup_by_nick(%s) (in %s) -\n", nickname,
  2391.     (ab && ab->filename) ? ab->filename : "?"));
  2392.  
  2393.     if(!ab || !nickname || !nickname[0])
  2394.       return NULL;
  2395.  
  2396.     hash = ab_hash(nickname, (a_c_arg_t)ab->htable_size);
  2397.     uid  = ab_uid(nickname);
  2398.  
  2399.     last_one = (EntryRef *)NULL;
  2400.  
  2401.     for(ind = ab->hash_by_nick->harray[hash];
  2402.     ind != NO_NEXT &&
  2403.             (entry = adrbk_get_entryref(ab, (a_c_arg_t)ind, Normal));
  2404.     ind = entry->next_nick){
  2405.  
  2406.     if(entry->uid_nick == uid){
  2407.         ae = adrbk_get_ae(ab, (a_c_arg_t)ind, Normal);
  2408.         /*
  2409.          * Guard against the unlikely case where two nicknames have the
  2410.          * same hash and uid, but are actually different.
  2411.          */
  2412.         if(strucmp(ae->nickname, nickname) == 0){
  2413.         last_one = entry;
  2414.         last_ind = ind;
  2415.         }
  2416.     }
  2417.     }
  2418.  
  2419.     /* no such nickname */
  2420.     if(last_one == (EntryRef *)NULL)
  2421.       return((AdrBk_Entry *)NULL);
  2422.  
  2423.     if(entry_num)
  2424.       *entry_num = last_ind;
  2425.  
  2426.     return(adrbk_get_ae(ab, (a_c_arg_t)last_ind, Normal));
  2427. }
  2428.  
  2429.  
  2430. /*
  2431.  * Look up an entry in the address book given an address
  2432.  *
  2433.  * Args: ab       -- the address book
  2434.  *       address  -- address to match
  2435.  *      entry_num -- if matched, return entry_num of match here
  2436.  *
  2437.  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
  2438.  *
  2439.  * Note:  When there are multiple occurrences of an address in an addressbook,
  2440.  * which there will be if more than one nickname points to same address, then
  2441.  * we want this to match the first occurrence so that the fcc you get will
  2442.  * be predictable.  Because of the way the hash table is built (and needs to
  2443.  * be built) we need to look for the last occurrence of uid within the list
  2444.  * a hash table entry points to instead of the first occurrence.
  2445.  */
  2446. AdrBk_Entry *
  2447. adrbk_lookup_by_addr(ab, address, entry_num)
  2448.     AdrBk *ab;
  2449.     char  *address;
  2450.     adrbk_cntr_t *entry_num;
  2451. {
  2452.     adrbk_cntr_t hash;
  2453.     adrbk_uid_t uid;
  2454.     adrbk_cntr_t ind, last_ind;
  2455.     EntryRef *entry, *last_one;
  2456.  
  2457.     dprint(2, (debugfile, "- adrbk_lookup_by_addr(%s) (in %s) -\n", address,
  2458.     (ab && ab->filename) ? ab->filename : "?"));
  2459.  
  2460.     if(!ab || !address || !address[0])
  2461.       return NULL;
  2462.  
  2463.     hash = ab_hash(address, (a_c_arg_t)ab->htable_size);
  2464.     uid  = ab_uid(address);
  2465.  
  2466.     last_one = (EntryRef *)NULL;
  2467.  
  2468.     for(ind = ab->hash_by_addr->harray[hash];
  2469.     ind != NO_NEXT &&
  2470.             (entry = adrbk_get_entryref(ab, (a_c_arg_t)ind, Normal));
  2471.     ind = entry->next_addr){
  2472.  
  2473.     if(entry->uid_addr == uid){
  2474.         last_one = entry;
  2475.         last_ind = ind;
  2476.     }
  2477.     }
  2478.  
  2479.     /* no such address */
  2480.     if(last_one == (EntryRef *)NULL)
  2481.       return((AdrBk_Entry *)NULL);
  2482.  
  2483.     if(entry_num)
  2484.       *entry_num = last_ind;
  2485.  
  2486.     return(adrbk_get_ae(ab, (a_c_arg_t)last_ind, Normal));
  2487. }
  2488.  
  2489.  
  2490. /*
  2491.  * Format a full name.
  2492.  *
  2493.  * Args: fullname -- full name out of address book for formatting
  2494.  *
  2495.  * Result:  Returns pointer to static internal buffer containing name
  2496.  * formatted for a mail header.
  2497.  *
  2498.  * We need this because we store full names as Last, First.
  2499.  * If the name has no comma, then no change is made.
  2500.  * Otherwise the text before the first comma is moved to the end and
  2501.  * the comma is deleted.
  2502.  */
  2503. char *
  2504. adrbk_formatname(fullname)
  2505.     char *fullname;
  2506. {
  2507.     char       *comma, *p, *charset = NULL;
  2508.     char        buf[MAX_FULLNAME + 10];
  2509.     static char new_name[MAX_FULLNAME];
  2510.     int         need_to_encode = 0;
  2511.  
  2512.     /*
  2513.      * It's difficult to find comma when it is encoded, so decode it
  2514.      * and search for comma.  Note, we can't handle a fullname with
  2515.      * multiple charsets in it.
  2516.      */
  2517.     p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,fullname,&charset);
  2518.     if(p == fullname){
  2519.     if(charset)
  2520.       fs_give((void **)&charset);
  2521.  
  2522.     charset = NULL;
  2523.     }
  2524.     else
  2525.       need_to_encode++;
  2526.  
  2527.     fullname = p;  /* overloading fullname, now decoded */
  2528.  
  2529.     if(fullname[0] != '"'  && (comma = strindex(fullname, ',')) != NULL){
  2530.         int last_name_len = comma - fullname;
  2531.         comma++;
  2532.         while(*comma && isspace(*comma))
  2533.       comma++;
  2534.  
  2535.         strcpy(new_name, comma);
  2536.         strcat(new_name, " ");
  2537.         strncat(new_name, fullname, last_name_len); 
  2538.     }
  2539.     else
  2540.       strcpy(new_name, fullname);
  2541.     
  2542.     if(need_to_encode){
  2543.     char *s;
  2544.  
  2545.     /* re-encode in original charset */
  2546.     s = rfc1522_encode(buf, (unsigned char *)new_name,
  2547.         charset ? charset : ps_global->VAR_CHAR_SET);
  2548.     if(s != new_name)
  2549.       strncpy(new_name, s, MAX_FULLNAME);
  2550.     
  2551.     new_name[MAX_FULLNAME-1] = '\0';
  2552.     if(charset)
  2553.       fs_give((void **)&charset);
  2554.     }
  2555.  
  2556.     return(new_name);
  2557. }
  2558.  
  2559.  
  2560. /*
  2561.  * Clear reference flags in preparation for a recursive lookup.
  2562.  *
  2563.  * For loop detection during address book look up.  This clears all the 
  2564.  * referenced flags, then as the lookup proceeds the referenced flags can 
  2565.  * be checked and set.
  2566.  */
  2567. void
  2568. adrbk_clearrefs(ab)
  2569. AdrBk *ab;
  2570.     {
  2571.     dprint(9, (debugfile, "- adrbk_clearrefs -\n"));
  2572.  
  2573.     if(!ab)
  2574.       return;
  2575.  
  2576.     /*
  2577.      * We only have to clear the references in cached ae's, since newly
  2578.      * created ae's start out with cleared references.  This should speed
  2579.      * things up considerably for large addrbooks.
  2580.      */
  2581.     clearrefs_in_cached_aes(ab);
  2582. }
  2583.  
  2584.  
  2585. /*
  2586.  *  Allocate a new AdrBk_Entry
  2587.  */
  2588. AdrBk_Entry *
  2589. adrbk_newentry()
  2590. {
  2591.     AdrBk_Entry *a;
  2592.  
  2593.     a = (AdrBk_Entry *)fs_get(sizeof(AdrBk_Entry));
  2594.     a->nickname    = empty;
  2595.     a->fullname    = empty;
  2596.     a->addr.addr   = empty;
  2597.     a->fcc         = empty;
  2598.     a->extra       = empty;
  2599.     a->tag         = NotSet;
  2600.     a->referenced  = 0;
  2601.  
  2602.     return(a);
  2603. }
  2604.  
  2605.  
  2606. AdrBk_Entry *
  2607. copy_ae(src)
  2608.     AdrBk_Entry *src;
  2609. {
  2610.     AdrBk_Entry *a;
  2611.  
  2612.     a = adrbk_newentry();
  2613.     a->tag = src->tag;
  2614.     a->nickname = cpystr(src->nickname ? src->nickname : "");
  2615.     a->fullname = cpystr(src->fullname ? src->fullname : "");
  2616.     a->fcc      = cpystr(src->fcc ? src->fcc : "");
  2617.     a->extra    = cpystr(src->extra ? src->extra : "");
  2618.     if(a->tag == Single)
  2619.       a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
  2620.     else if(a->tag == List){
  2621.     char **p;
  2622.     int    i, n;
  2623.  
  2624.     /* count list */
  2625.     for(p = src->addr.list; p && *p; p++)
  2626.       ;/* do nothing */
  2627.     
  2628.     if(p == NULL)
  2629.       n = 0;
  2630.     else
  2631.       n = p - src->addr.list;
  2632.  
  2633.     a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
  2634.     for(i = 0; i < n; i++)
  2635.       a->addr.list[i] = cpystr(src->addr.list[i]);
  2636.     
  2637.     a->addr.list[n] = NULL;
  2638.     }
  2639.  
  2640.     return(a);
  2641. }
  2642.  
  2643.  
  2644.  
  2645. /*
  2646.  * Add an entry to the address book, or modify an existing entry
  2647.  *
  2648.  * Args: ab       -- address book to add to
  2649.  *  old_entry_num -- the entry we want to modify.  If this is NO_NEXT, then
  2650.  *                    we look up the nickname passed in to see if that's the
  2651.  *                    entry to modify, else it is a new entry.
  2652.  *       nickname -- the nickname for new entry
  2653.  *       fullname -- the fullname for new entry
  2654.  *       address  -- the address for new entry
  2655.  *       fcc      -- the fcc for new entry
  2656.  *       extra    -- the extra field for new entry
  2657.  *       tag      -- the type of new entry
  2658.  *  new_entry_num -- return entry_num of new or modified entry here
  2659.  *  resort_happened -- means that more than just the current entry changed,
  2660.  *                     either something was added or order was changed
  2661.  *
  2662.  * Result: return code:  0 all went well
  2663.  *                      -2 error writing address book, check errno
  2664.  *                -3 no modification, the tag given didn't match
  2665.  *                         existing tag
  2666.  *                      -4 tabs are in one of the fields passed in
  2667.  *
  2668.  * If the nickname exists in the address book already, the operation is
  2669.  * considered a modification even if the case does not match exactly,
  2670.  * otherwise it is an add.  The entry the operation occurs on is returned
  2671.  * in new.  All fields are set to those passed in; that is, passing in NULL
  2672.  * even on a modification will set those fields to NULL as opposed to leaving
  2673.  * them unchanged.  It is acceptable to pass in the current strings
  2674.  * in the entry in the case of modification.  For address lists, the
  2675.  * structure passed in is what is used, so the storage has to all have
  2676.  * come from malloc or fs_get().  If the pointer passed in is the same as
  2677.  * the current field, no change is made.
  2678.  */
  2679. int
  2680. adrbk_add(ab, old_entry_num, nickname, fullname, address, fcc, extra, tag,
  2681.       new_entry_num, resort_happened)
  2682.     AdrBk        *ab;
  2683.     a_c_arg_t     old_entry_num;
  2684.     char         *nickname,
  2685.          *fullname,
  2686.          *address, /* address can be char **, too */
  2687.          *fcc,
  2688.          *extra;
  2689.     Tag           tag;
  2690.     adrbk_cntr_t *new_entry_num;
  2691.     int          *resort_happened;
  2692. {
  2693.     AdrBk_Entry *a;
  2694.     AdrBk_Entry *ae;
  2695.     adrbk_cntr_t old_enum;
  2696.     adrbk_cntr_t new_enum;
  2697.     int (*cmp_func)();
  2698.     int retval;
  2699.     int need_write = 0;
  2700.  
  2701.     if(!ab)
  2702.       return -2;
  2703.  
  2704.     /* ---- Make sure there are no tabs in the stuff to add ------*/
  2705.     if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
  2706.        (fullname != NULL && strindex(fullname, TAB) != NULL) ||
  2707.        (fcc != NULL && strindex(fcc, TAB) != NULL) ||
  2708.        (tag == Single && address != NULL && strindex(address, TAB) != NULL))
  2709.         return -4;
  2710.  
  2711.     /*
  2712.      * Are we adding or updating ?
  2713.      *
  2714.      * If old_entry_num was passed in, we're updating that.  If nickname
  2715.      * already exists, we're updating that entry.  Otherwise, this is an add.
  2716.      */
  2717.     if((adrbk_cntr_t)old_entry_num != NO_NEXT){
  2718.     ae = adrbk_get_ae(ab, old_entry_num, Normal);
  2719.     if(ae)
  2720.       old_enum = (adrbk_cntr_t)old_entry_num;
  2721.     }
  2722.     else
  2723.       ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
  2724.  
  2725.     if(ae == NULL){  /*----- adding a new entry ----*/
  2726.  
  2727.         ae            = adrbk_newentry();
  2728.         ae->nickname  = nickname ? cpystr(nickname) : nickname;
  2729.         ae->fullname  = fullname ? cpystr(fullname) : fullname;
  2730.         ae->fcc       = fcc      ? cpystr(fcc)      : fcc;
  2731.         ae->extra     = extra    ? cpystr(extra)    : extra;
  2732.     ae->tag       = tag;
  2733.  
  2734.     if(tag == Single)
  2735.           ae->addr.addr = cpystr(address);
  2736.     else
  2737.       ae->addr.list = (char **)NULL;
  2738.  
  2739.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  2740.                         cmp_ae_by_full_lists_last :
  2741.            (ab->sort_rule == AB_SORT_RULE_FULL) ?
  2742.                         cmp_ae_by_full :
  2743.            (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  2744.                         cmp_ae_by_nick_lists_last :
  2745.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  2746.                         cmp_ae_by_nick;
  2747.  
  2748.     if(ab->sort_rule == AB_SORT_RULE_NONE)  /* put it last */
  2749.       new_enum = ab->count;
  2750.     else  /* Find slot for it */
  2751.       for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t)new_enum, Normal);
  2752.           a != (AdrBk_Entry *)NULL;
  2753.           a = adrbk_get_ae(ab, (a_c_arg_t)(++new_enum), Normal)){
  2754.             if((*cmp_func)((QSType *)&a, (QSType *)&ae) >= 0)
  2755.             break;
  2756.       }
  2757.     /* Insert ae before entry new_enum. */
  2758.     set_inserted_entryref(ab, (a_c_arg_t)new_enum, ae);
  2759.  
  2760.         /*---- return in pointer if requested -----*/
  2761.         if(new_entry_num)
  2762.       *new_entry_num = new_enum;
  2763.  
  2764.         if(resort_happened)
  2765.       *resort_happened = 1;
  2766.  
  2767.     retval = adrbk_write(ab, NULL, 0);
  2768.     if(retval == 0)
  2769.       exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
  2770.     }
  2771.     else{
  2772.         /*----- Updating an existing entry ----*/
  2773.  
  2774.     if(ae->tag != tag)
  2775.       return -3;
  2776.  
  2777.     /* Lock this entry in the entryref cache */
  2778.     (void)adrbk_get_entryref(ab, (a_c_arg_t)old_enum, Lock);
  2779.  
  2780.         /*
  2781.      * Instead of just freeing and reallocating here we attempt to reuse
  2782.      * the space that was already allocated if possible.
  2783.      */
  2784.     if(ae->nickname != nickname
  2785.        && ae->nickname != NULL
  2786.        && nickname != NULL
  2787.        && strcmp(nickname, ae->nickname) != 0){
  2788.         need_write++;
  2789.         /* can use already alloc'd space */
  2790.             if(ae->nickname != NULL && nickname != NULL &&
  2791.            strlen(nickname) <= strlen(ae->nickname)){
  2792.  
  2793.                 strcpy(ae->nickname, nickname);
  2794.             }
  2795.         else{
  2796.                 if(ae->nickname != NULL && ae->nickname != empty)
  2797.                   fs_give((void **)&ae->nickname);
  2798.  
  2799.                 ae->nickname = nickname ? cpystr(nickname) : nickname;
  2800.         }
  2801.         }
  2802.  
  2803.     if(ae->fullname != fullname
  2804.        && ae->fullname != NULL
  2805.        && fullname != NULL
  2806.        && strcmp(fullname, ae->fullname) != 0){
  2807.         need_write++;
  2808.             if(ae->fullname != NULL && fullname != NULL &&
  2809.            strlen(fullname) <= strlen(ae->fullname)){
  2810.  
  2811.                 strcpy(ae->fullname, fullname);
  2812.             }
  2813.         else{
  2814.                 if(ae->fullname != NULL && ae->fullname != empty)
  2815.                   fs_give((void **)&ae->fullname);
  2816.  
  2817.                 ae->fullname = fullname ? cpystr(fullname) : fullname;
  2818.         }
  2819.         }
  2820.  
  2821.     if(ae->fcc != fcc
  2822.        && ae->fcc != NULL
  2823.        && fcc != NULL
  2824.        && strcmp(fcc, ae->fcc) != 0){
  2825.         need_write++;
  2826.             if(ae->fcc != NULL && fcc != NULL &&
  2827.            strlen(fcc) <= strlen(ae->fcc)){
  2828.  
  2829.                 strcpy(ae->fcc, fcc);
  2830.             }
  2831.         else{
  2832.                 if(ae->fcc != NULL && ae->fcc != empty)
  2833.                   fs_give((void **)&ae->fcc);
  2834.  
  2835.                 ae->fcc = fcc ? cpystr(fcc) : fcc;
  2836.         }
  2837.         }
  2838.  
  2839.     if(ae->extra != extra
  2840.        && ae->extra != NULL
  2841.        && extra != NULL
  2842.        && strcmp(extra, ae->extra) != 0){
  2843.         need_write++;
  2844.             if(ae->extra != NULL && extra != NULL &&
  2845.            strlen(extra) <= strlen(ae->extra)){
  2846.  
  2847.                 strcpy(ae->extra, extra);
  2848.             }
  2849.         else{
  2850.                 if(ae->extra != NULL && ae->extra != empty)
  2851.                   fs_give((void **)&ae->extra);
  2852.  
  2853.                 ae->extra = extra ? cpystr(extra) : extra;
  2854.         }
  2855.         }
  2856.  
  2857.     if(tag == Single){
  2858.             /*---- Single ----*/
  2859.         if(ae->addr.addr != address
  2860.            && ae->addr.addr != NULL
  2861.            && address != NULL
  2862.            && strcmp(address, ae->addr.addr) != 0){
  2863.         need_write++;
  2864.         if(ae->addr.addr != NULL && address != NULL &&
  2865.            strlen(address) <= strlen(ae->addr.addr)){
  2866.  
  2867.             strcpy(ae->addr.addr, address);
  2868.         }
  2869.         else{
  2870.             if(ae->addr.addr != NULL && ae->addr.addr != empty)
  2871.               fs_give((void **)&ae->addr.addr);
  2872.  
  2873.             ae->addr.addr = address ? cpystr(address) : address;
  2874.         }
  2875.         }
  2876.     }
  2877.     else{
  2878.             /*---- List -----*/
  2879.             /*
  2880.          * We don't mess with lists here.
  2881.          * The caller has to do it with adrbk_listadd().
  2882.          */
  2883.         ;/* do nothing */
  2884.     }
  2885.  
  2886.  
  2887.         /*---------- Make sure it's still in order ---------*/
  2888.     /*
  2889.      * old_enum is where ae is currently located
  2890.      * put it where it belongs
  2891.      */
  2892.     if(need_write)
  2893.       new_enum = re_sort_particular_entryref(ab, (a_c_arg_t)old_enum);
  2894.     else
  2895.       new_enum = old_enum;
  2896.  
  2897.         /*---- return in pointer if requested -----*/
  2898.         if(new_entry_num)
  2899.       *new_entry_num = new_enum;
  2900.  
  2901.         if(resort_happened)
  2902.       *resort_happened = (old_enum != new_enum);
  2903.  
  2904.     if(need_write)
  2905.       retval = adrbk_write(ab, NULL, 0);
  2906.     else{
  2907.         (void)adrbk_get_entryref(ab, (a_c_arg_t)old_enum, Unlock);
  2908.         retval = 0;
  2909.     }
  2910.  
  2911.     /*
  2912.      * If it got re-sorted we just throw in the towel and unexpand
  2913.      * all the lists.  Maybe someday we'll fix it to try to track
  2914.      * the numbers of the expanded lists.
  2915.      */
  2916.     if(old_enum != new_enum && retval == 0)
  2917.       exp_free(ab->exp);
  2918.     }
  2919.  
  2920.     return(retval);
  2921. }
  2922.  
  2923.  
  2924. /*
  2925.  * The entire address book is assumed sorted correctly except perhaps for
  2926.  * entry number cur.  Put it in the correct place.  Return the new entry
  2927.  * number for cur.
  2928.  */
  2929. adrbk_cntr_t
  2930. re_sort_particular_entryref(ab, cur)
  2931.     AdrBk *ab;
  2932.     a_c_arg_t cur;
  2933. {
  2934.     AdrBk_Entry  *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
  2935.     long small_enough;
  2936.     adrbk_cntr_t big_enough;
  2937.     adrbk_cntr_t new_entry_num;
  2938.     int (*cmp_func)();
  2939.  
  2940.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  2941.                         cmp_ae_by_full_lists_last :
  2942.            (ab->sort_rule == AB_SORT_RULE_FULL) ?
  2943.                         cmp_ae_by_full :
  2944.            (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  2945.                         cmp_ae_by_nick_lists_last :
  2946.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  2947.                         cmp_ae_by_nick;
  2948.  
  2949.     new_entry_num = (adrbk_cntr_t)cur;
  2950.  
  2951.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  2952.       return(new_entry_num);
  2953.  
  2954.     ae_cur = adrbk_get_ae(ab, cur, Lock);
  2955.  
  2956.     if(cur > 0)
  2957.       ae_prev  = adrbk_get_ae(ab, cur - 1, Lock);
  2958.  
  2959.     if(cur < ab->count -1)
  2960.       ae_next  = adrbk_get_ae(ab, cur + 1, Lock);
  2961.  
  2962.     /*
  2963.      * A possible optimization here would be to implement some sort of
  2964.      * binary search to find where it goes instead of stepping through the
  2965.      * entries one at a time.  That way, it might be faster, and, we wouldn't
  2966.      * have to page in as much of the entryref array.  Another optimization
  2967.      * is a way to access cached stuff only, so we could look through all
  2968.      * the cached stuff first before looking at stuff on disk.
  2969.      */
  2970.     if(cur > 0 &&
  2971.        (*cmp_func)((QSType *)&ae_cur,(QSType *)&ae_prev) < 0){
  2972.     /*--- Out of order, needs to be moved up ----*/
  2973.     for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
  2974.       ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t)small_enough,Normal);
  2975.       if((*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_small_enough) >= 0)
  2976.         break;
  2977.     }
  2978.     new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
  2979.     set_moved_entryref(cur, (a_c_arg_t)new_entry_num);
  2980.     }
  2981.     else if(cur < ab->count - 1 &&
  2982.     (*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_next) > 0){
  2983.     /*---- Out of order needs, to be moved towards end of list ----*/
  2984.     for(big_enough = (adrbk_cntr_t)(cur + 2);
  2985.         big_enough < ab->count;
  2986.         big_enough++){
  2987.       ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t)big_enough, Normal);
  2988.       if((*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_big_enough) <= 0)
  2989.         break;
  2990.     }
  2991.     new_entry_num = big_enough - 1;
  2992.     set_moved_entryref(cur, (a_c_arg_t)big_enough);
  2993.     }
  2994.  
  2995.     (void)adrbk_get_ae(ab, cur - 1, Unlock);
  2996.     (void)adrbk_get_ae(ab, cur + 1, Unlock);
  2997.  
  2998.     return(new_entry_num);
  2999. }
  3000.  
  3001.  
  3002. /*
  3003.  * Delete an entry from the address book
  3004.  *
  3005.  * Args: ab        -- the address book
  3006.  *       entry_num -- entry to delete
  3007.  *    save_deleted -- save deleted as a #DELETED- entry
  3008.  *
  3009.  * Result: returns:  0 if all went well
  3010.  *                  -1 if there is no such entry
  3011.  *                  -2 error writing address book, check errno
  3012.  */
  3013. int
  3014. adrbk_delete(ab, entry_num, save_deleted)
  3015.     AdrBk    *ab;
  3016.     a_c_arg_t entry_num;
  3017.     int       save_deleted;
  3018. {
  3019.     int retval;
  3020.  
  3021.     if(!ab)
  3022.       return -2;
  3023.  
  3024.     (void)adrbk_get_entryref(ab, entry_num, save_deleted ? SaveDelete : Delete);
  3025.  
  3026.     retval = adrbk_write(ab, NULL, 0);
  3027.  
  3028.     if(retval == 0)
  3029.       exp_del_nth(ab->exp, entry_num);
  3030.     
  3031.     return(retval);
  3032. }
  3033.  
  3034.  
  3035. /*
  3036.  * Delete an address out of an address list
  3037.  *
  3038.  * Args: ab    -- the address book
  3039.  *       entry -- the address list we are deleting from
  3040.  *       addr  -- address in above list to be deleted
  3041.  *
  3042.  * Result: 0: Deletion complete, address book written
  3043.  *        -1: Address for deletion not found
  3044.  *        -2: Error writing address book. Check errno.
  3045.  *
  3046.  * The address to be deleted is located by matching the string.
  3047.  *
  3048.  * This doesn't invalidate any of the AEMgr cache or anything since this
  3049.  * entry is still there when we're done and we've updated the cache
  3050.  * entry by deleting it below.
  3051.  */
  3052. int
  3053. adrbk_listdel(ab, entry_num, addr)
  3054.     AdrBk       *ab;
  3055.     a_c_arg_t   entry_num;
  3056.     char        *addr;
  3057. {
  3058.     char **p, *to_free;
  3059.     AdrBk_Entry *entry;
  3060.  
  3061.     if(!ab || entry_num >= ab->count)
  3062.       return -2;
  3063.  
  3064.     if(!addr)
  3065.       return -1;
  3066.  
  3067.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3068.  
  3069.     if(entry->tag != List){
  3070.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3071.         return -1;
  3072.     }
  3073.  
  3074.     for(p = entry->addr.list; *p; p++) 
  3075.       if(strcmp(*p, addr) == 0)
  3076.         break;
  3077.  
  3078.     if(*p == NULL)
  3079.       return -1;
  3080.  
  3081.     /* note storage to be freed */
  3082.     if(*p != empty)
  3083.       to_free = *p;
  3084.     else
  3085.       to_free = NULL;
  3086.  
  3087.     /* slide all the entries below up (including NULL) */
  3088.     for(; *p; p++)
  3089.       *p = *(p+1);
  3090.  
  3091.     if(to_free)
  3092.       fs_give((void **)&to_free);
  3093.  
  3094.     return(adrbk_write(ab, NULL, 0));
  3095. }
  3096.  
  3097.  
  3098. /*
  3099.  * Delete all addresses out of an address list
  3100.  *
  3101.  * Args: ab    -- the address book
  3102.  *       entry -- the address list we are deleting from
  3103.  *
  3104.  * Result: 0: Deletion complete, address book written
  3105.  *        -1: Address for deletion not found
  3106.  *        -2: Error writing address book. Check errno.
  3107.  *
  3108.  * This doesn't invalidate any of the AEMgr cache or anything since this
  3109.  * entry is still there when we're done and we've updated the cache
  3110.  * entry by deleting it below.
  3111.  */
  3112. int
  3113. adrbk_listdel_all(ab, entry_num)
  3114.     AdrBk       *ab;
  3115.     a_c_arg_t   entry_num;
  3116. {
  3117.     char **p;
  3118.     AdrBk_Entry *entry;
  3119.  
  3120.     if(!ab || entry_num >= ab->count)
  3121.       return -2;
  3122.  
  3123.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3124.  
  3125.     if(entry->tag != List){
  3126.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3127.         return -1;
  3128.     }
  3129.  
  3130.     /* free old list */
  3131.     for(p = entry->addr.list; p && *p; p++) 
  3132.       if(*p != empty)
  3133.         fs_give((void **)p);
  3134.     
  3135.     if(entry->addr.list)
  3136.       fs_give((void **)&(entry->addr.list));
  3137.  
  3138.     entry->addr.list = NULL;
  3139.  
  3140.     return(adrbk_write(ab, NULL, 0));
  3141. }
  3142.  
  3143.  
  3144. /*
  3145.  * Add one address to an already existing address list
  3146.  *
  3147.  * Args: ab       -- the address book
  3148.  *       entry    -- the address list we are adding to
  3149.  *       addr     -- address to be added
  3150.  *
  3151.  * Result: returns 0 : addition made, address book written
  3152.  *                -1 : addition to non-list attempted
  3153.  *                -2 : error writing address book -- check errno
  3154.  */
  3155. int
  3156. adrbk_listadd(ab, entry_num, addr)
  3157.     AdrBk       *ab;
  3158.     a_c_arg_t    entry_num;
  3159.     char        *addr;
  3160. {
  3161.     char **p;
  3162.     int    n;
  3163.     AdrBk_Entry *entry;
  3164.  
  3165.     if(!ab || entry_num >= ab->count)
  3166.       return -2;
  3167.  
  3168.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3169.  
  3170.     if(entry->tag != List){
  3171.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3172.         return -1;
  3173.     }
  3174.  
  3175.     /*--- count up size of list ------*/    
  3176.     for(p = entry->addr.list; p != NULL && *p != NULL; p++)
  3177.       ;/* do nothing */
  3178.  
  3179.     /*----- make room at end of list for it ------*/
  3180.     if(entry->addr.list == NULL){
  3181.         entry->addr.list = (char **)fs_get(2 * sizeof(char *));
  3182.         n = 1;
  3183.     }
  3184.     else{
  3185.         n = p - entry->addr.list;
  3186.         n++;
  3187.     /* n is size of list, +1 for NULL */
  3188.         fs_resize((void **)&entry->addr.list, sizeof(char *) * (n + 1));
  3189.     }
  3190.  
  3191.     /*----- Put it at the end -------*/
  3192.     (entry->addr.list)[n-1] = cpystr(addr);
  3193.     (entry->addr.list)[n]   = NULL;
  3194.  
  3195.     /*---- sort it into the correct place ------*/
  3196.     if(ab->sort_rule != AB_SORT_RULE_NONE)
  3197.       sort_addr_list(entry->addr.list);
  3198.  
  3199.     return(adrbk_write(ab, NULL, 0));
  3200. }
  3201.  
  3202.  
  3203. /*
  3204.  * Add a list of addresses to an already existing address list
  3205.  *
  3206.  * Args: ab       -- the address book
  3207.  *       entry    -- the address list we are adding to
  3208.  *       addrs    -- address list to be added
  3209.  *
  3210.  * Result: returns 0 : addition made, address book written
  3211.  *                -1 : addition to non-list attempted
  3212.  *                -2 : error writing address book -- check errno
  3213.  */
  3214. int
  3215. adrbk_nlistadd(ab, entry_num, addrs)
  3216. AdrBk       *ab;
  3217. a_c_arg_t    entry_num;
  3218. char       **addrs;
  3219. {
  3220.     char **p;
  3221.     int    cur_size, size_of_additional_list, new_size;
  3222.     int    i;
  3223.     AdrBk_Entry *entry;
  3224.  
  3225.     if(!ab || entry_num >= ab->count)
  3226.       return -2;
  3227.  
  3228.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3229.  
  3230.     if(entry->tag != List){
  3231.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3232.         return -1;
  3233.     }
  3234.  
  3235.     /* count up size of existing list */    
  3236.     for(p = entry->addr.list; p != NULL && *p != NULL; p++)
  3237.       ;/* do nothing */
  3238.  
  3239.     cur_size = p - entry->addr.list;
  3240.  
  3241.     /* count up size of new list */    
  3242.     for(p = addrs; p != NULL && *p != NULL; p++)
  3243.       ;/* do nothing */
  3244.  
  3245.     size_of_additional_list = p - addrs;
  3246.     new_size = cur_size + size_of_additional_list;
  3247.  
  3248.     /* make room at end of list for it */
  3249.     fs_resize((void **)&entry->addr.list, sizeof(char *) * (new_size + 1));
  3250.  
  3251.     /* Put new list at the end */
  3252.     for(i = cur_size; i < new_size; i++)
  3253.       (entry->addr.list)[i] = cpystr(addrs[i - cur_size]);
  3254.  
  3255.     (entry->addr.list)[new_size] = NULL;
  3256.  
  3257.     /*---- sort it into the correct place ------*/
  3258.     if(ab->sort_rule != AB_SORT_RULE_NONE)
  3259.       sort_addr_list(entry->addr.list);
  3260.  
  3261.     return(adrbk_write(ab, NULL, 0));
  3262. }
  3263.  
  3264.  
  3265. /*
  3266.  * Close address book
  3267.  *
  3268.  * All that is done here is to free the storage, since the address book is 
  3269.  * rewritten on every change.
  3270.  */
  3271. void
  3272. adrbk_close(ab)
  3273.     AdrBk *ab;
  3274. {
  3275.     if(!ab)
  3276.       return;
  3277.  
  3278.     clear_entryref_cache(ab);
  3279.     if(ab->fp)
  3280.       (void)fclose(ab->fp);
  3281.  
  3282.     if(ab->fp_hash)
  3283.       (void)fclose(ab->fp_hash);
  3284.     
  3285.     if(ab->head_cache_elem)
  3286.       fs_give((void **)&ab->head_cache_elem);
  3287.  
  3288.     if(ab->tail_cache_elem)
  3289.       fs_give((void **)&ab->tail_cache_elem);
  3290.  
  3291.     if(ab->n_ae_cached_in_this_bucket)
  3292.       fs_give((void **)&ab->n_ae_cached_in_this_bucket);
  3293.  
  3294.     if(ab->hash_by_nick)
  3295.       free_ab_adrhash(&ab->hash_by_nick);
  3296.  
  3297.     if(ab->hash_by_addr)
  3298.       free_ab_adrhash(&ab->hash_by_addr);
  3299.  
  3300.     if(ab->filename)
  3301.       fs_give((void**)&ab->filename);
  3302.  
  3303.     if(ab->orig_filename)
  3304.       fs_give((void**)&ab->orig_filename);
  3305.  
  3306.     if(ab->temp_filename)
  3307.       fs_give((void**)&ab->temp_filename);
  3308.  
  3309.     if(ab->delete_hashfile && ab->hashfile)
  3310.       unlink(ab->hashfile);
  3311.  
  3312.     if(ab->hashfile)
  3313.       fs_give((void**)&ab->hashfile);
  3314.  
  3315.     if(ab->temp_hashfile)
  3316.       fs_give((void**)&ab->temp_hashfile);
  3317.  
  3318.     if(ab->exp){
  3319.     exp_free(ab->exp);
  3320.     fs_give((void **)&ab->exp);  /* free head of list, too */
  3321.     }
  3322.  
  3323.     if(ab->checks){
  3324.     exp_free(ab->checks);
  3325.     fs_give((void **)&ab->checks);  /* free head of list, too */
  3326.     }
  3327.  
  3328.     fs_give((void **)&ab);
  3329. }
  3330.  
  3331.  
  3332. void
  3333. adrbk_partial_close(ab)
  3334.     AdrBk *ab;
  3335. {
  3336.     init_entryref_cache(ab);
  3337.     exp_free(ab->exp);  /* leaves head of list */
  3338.     exp_free(ab->checks);  /* leaves head of list */
  3339. }
  3340.  
  3341.  
  3342. void
  3343. free_ab_entryref(ab, entry)
  3344.     AdrBk *ab;
  3345.     EntryRef *entry;
  3346. {
  3347.     if(entry->ae)
  3348.       free_ae(ab, &entry->ae);
  3349.  
  3350.     fs_give((void **)&entry);
  3351. }
  3352.  
  3353.  
  3354. static adrbk_cntr_t tot_for_percent;
  3355. static adrbk_cntr_t entry_num_for_percent;
  3356. #define AB_RENAMED_ABOOK  0x1
  3357. #define AB_RENAMED_HASH   0x2
  3358.  
  3359. /*
  3360.  * Write out the address book.
  3361.  *
  3362.  * Format is as in comment in the adrbk_open routine.  Lines are wrapped
  3363.  * to be under 80 characters.  This is called on every change to the
  3364.  * address book.  Write is first to a temporary file,
  3365.  * which is then renamed to be the real address book so that we won't
  3366.  * destroy the real address book in case of something like a full file
  3367.  * system.
  3368.  *
  3369.  * Writing a temp file and then renaming has the bad side affect of
  3370.  * destroying links.  It also overrides any read only permissions on
  3371.  * the mail file since rename ignores such permissions.  However, we
  3372.  * handle readonly-ness in addrbook.c before we call this.
  3373.  * We retain the permissions by doing a stat on the old file and a
  3374.  * chmod on the new one.
  3375.  *
  3376.  * Returns:   0 write was successful
  3377.  *           -2 write failed
  3378.  *           -5 interrupted
  3379.  */
  3380. int
  3381. adrbk_write(ab, sort_array, be_quiet)
  3382.     AdrBk *ab;
  3383.     adrbk_cntr_t *sort_array;
  3384.     int be_quiet;
  3385. {
  3386.     FILE                  *ab_stream = NULL,
  3387.                           *fp_for_hash = NULL;
  3388.     EntryRef              *entry;
  3389.     AdrBk_Entry           *ae = NULL;
  3390.     adrbk_cntr_t           entry_num;
  3391.     time_t                 mtime;
  3392.     long                   saved_deleted_cnt;
  3393.     adrbk_cntr_t           hash,
  3394.                            new_htable_size;
  3395. #ifndef    DOS
  3396.     void                  (*save_sighup)();
  3397. #endif
  3398.     int                   max_nick = 0,
  3399.               max_full = 0, full_two = 0, full_three = 0,
  3400.               max_fcc = 0, fcc_two = 0, fcc_three = 0,
  3401.               max_addr = 0, addr_two = 0, addr_three = 0,
  3402.               this_nick_width, this_full_width, this_addr_width,
  3403.               this_fcc_width;
  3404.     int                   progress = 0, interrupt_happened = 0, we_cancel = 0;
  3405.     WIDTH_INFO_S         *widths;
  3406.  
  3407.     if(!ab)
  3408.       return -2;
  3409.  
  3410.     dprint(2, (debugfile, "- adrbk_write(\"%s\") - writing %lu entries\n",
  3411.     ab->filename ? ab->filename : "", (unsigned long)ab->count));
  3412.     
  3413.     errno = 0;
  3414.  
  3415.     /* verify that file has not been changed by something else */
  3416.     if(ab->last_change_we_know_about != (time_t)(-1) &&
  3417.     (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
  3418.     ab->last_change_we_know_about != mtime){
  3419.     /* It has changed! */
  3420.     q_status_message(SM_ORDER | SM_DING, 5, 15,
  3421.         "Addrbook changed by another process, aborting our change to avoid damage...");
  3422.     dprint(1,
  3423.         (debugfile,
  3424. "adrbk_write: addrbook %s changed while we had it open, aborting adrbk_write\n",
  3425.         ab->filename));
  3426.     longjmp(addrbook_changed_unexpectedly, 1);
  3427.     /*NOTREACHED*/
  3428.     }
  3429.  
  3430.     if(!be_quiet){
  3431.     tot_for_percent = ab->count;
  3432.     entry_num_for_percent = 0;
  3433.     we_cancel = busy_alarm(1, "Saving address book",percent_abook_saved,1);
  3434.     }
  3435.  
  3436. #define TABWIDTH 8
  3437. #define INDENTSTR "   "
  3438. #define INDENT 3  /* length of INDENTSTR */
  3439.  
  3440. #ifndef    DOS
  3441.     save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
  3442. #endif
  3443.  
  3444.     if((ab_stream = fopen(ab->temp_filename, "w")) == NULL){
  3445. #ifndef    DOS
  3446.         (void)signal(SIGHUP, save_sighup);
  3447. #endif
  3448.         return -2;
  3449.     }
  3450.  
  3451.     /* Rebuild on-disk hash tables from incore hash tables */
  3452.     if((fp_for_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  3453.     dprint(2,
  3454.         (debugfile,
  3455.         "adrbk_write(%s): failed opening temp hashfile (%s)\n",
  3456.         ab->filename,
  3457.         "ab->temp_hashfile"));
  3458.  
  3459.     /* clear hash tables */
  3460.     new_htable_size = hashtable_size((a_c_arg_t)ab->count);
  3461.     if(new_htable_size == ab->htable_size){
  3462.     init_adrhash_array(ab->hash_by_nick, (a_c_arg_t)ab->htable_size);
  3463.     init_adrhash_array(ab->hash_by_addr, (a_c_arg_t)ab->htable_size);
  3464.     }
  3465.     else{
  3466.     free_ab_adrhash(&ab->hash_by_nick);
  3467.     free_ab_adrhash(&ab->hash_by_addr);
  3468.     ab->htable_size  = new_htable_size;
  3469.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  3470.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  3471.     }
  3472.  
  3473.     saved_deleted_cnt = ab->deleted_cnt;
  3474.  
  3475.     /* accept keyboard interrupts */
  3476.     intr_handling_on();
  3477.  
  3478.     if(write_hash_header(fp_for_hash, (a_c_arg_t)ab->htable_size))
  3479.       goto io_error;
  3480.  
  3481.     writing = 1;
  3482.  
  3483.     /*
  3484.      * If there was an entry that just got deleted, add it at top.
  3485.      */
  3486.     if(deleted_ae){
  3487.     struct tm *tm_now;
  3488.     time_t     now;
  3489.  
  3490.     now = time((time_t *)0);
  3491.     tm_now = localtime(&now);
  3492.     fprintf(ab_stream, "%s%02d/%02d/%02d#",
  3493.         DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1, tm_now->tm_mday);
  3494.     if(write_single_abook_entry(deleted_ae, ab_stream, &this_nick_width,
  3495.         &this_full_width, &this_addr_width, &this_fcc_width) == EOF)
  3496.       goto io_error;
  3497.     
  3498.     ab->deleted_cnt++;
  3499.     free_ae(ab, &deleted_ae);
  3500.     }
  3501.  
  3502.     /*
  3503.      * If there are any old deleted entries, copy them to new file.
  3504.      * We do so by scanning through the raw file looking for nicknames
  3505.      * which start with the deleted string.
  3506.      */
  3507.     if(saved_deleted_cnt > 0L){
  3508.     int rew = 1, c;
  3509.     FILE *fp_in;
  3510.     long offset, cnt_down, length;
  3511.     char *nickname;
  3512.  
  3513.     fp_in = ab->fp;
  3514.     if(!fp_in)
  3515.       goto io_error;
  3516.  
  3517.     cnt_down = saved_deleted_cnt;
  3518.     while(cnt_down > 0L
  3519.           && (nickname = skip_to_next_nickname(fp_in,&offset,NULL,&length,
  3520.                            rew,NULL)) != NULL){
  3521.         rew = 0;
  3522.  
  3523.         if(strncmp(nickname, DELETED, DELETED_LEN) == 0
  3524.            && isdigit(nickname[DELETED_LEN])
  3525.            && isdigit(nickname[DELETED_LEN+1])
  3526.            && nickname[DELETED_LEN+2] == '/'
  3527.            && isdigit(nickname[DELETED_LEN+3])
  3528.            && isdigit(nickname[DELETED_LEN+4])
  3529.            && nickname[DELETED_LEN+5] == '/'
  3530.            && isdigit(nickname[DELETED_LEN+6])
  3531.            && isdigit(nickname[DELETED_LEN+7])
  3532.            && nickname[DELETED_LEN+8] == '#'){
  3533.         long save_offset;
  3534.         int year, month, day;
  3535.         struct tm *tm_before;
  3536.         time_t     now, before;
  3537.  
  3538.         cnt_down--;
  3539.         now = time((time_t *)0);
  3540.         before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
  3541.         tm_before = localtime(&before);
  3542.         tm_before->tm_mon++;
  3543.  
  3544.         /*
  3545.          * Check to see if it is older than 100 days.
  3546.          */
  3547.         year  = atoi(&nickname[DELETED_LEN]);
  3548.         month = atoi(&nickname[DELETED_LEN+3]);
  3549.         day   = atoi(&nickname[DELETED_LEN+6]);
  3550.         if(year < 95)
  3551.           year += 100;  /* this breaks in year 2095 */
  3552.         
  3553.         /*
  3554.          * only write it if it is less than 100 days old
  3555.          */
  3556.         if(year > tm_before->tm_year
  3557.            || (year == tm_before->tm_year
  3558.                && month > tm_before->tm_mon)
  3559.            || (year == tm_before->tm_year
  3560.                && month == tm_before->tm_mon
  3561.                && day >= tm_before->tm_mday)){
  3562.  
  3563.             save_offset = ftell(fp_in);
  3564.             if(fseek(fp_in, offset, 0))
  3565.               goto io_error;
  3566.  
  3567.             while(length-- > 0L)
  3568.               if((c = getc(fp_in)) == EOF || putc(c, ab_stream) == EOF)
  3569.             goto io_error;
  3570.  
  3571.             if(fseek(fp_in, save_offset, 0))
  3572.               goto io_error;
  3573.         }
  3574.         else
  3575.           ab->deleted_cnt--;
  3576.         }
  3577.     }
  3578.  
  3579.     if(cnt_down != 0L)
  3580.       dprint(2, (debugfile,
  3581.         "adrbk_write(%s): Can't find %d commented deleteds\n",
  3582.         ab->filename, cnt_down));
  3583.     }
  3584.  
  3585.     for(entry_num = 0; entry_num < ab->count; entry_num++){
  3586.  
  3587.     entry_num_for_percent++;
  3588.  
  3589.     ALARM_BLIP();
  3590.  
  3591.     if(sort_array)
  3592.       entry = adrbk_get_entryref(ab, (a_c_arg_t)(sort_array[entry_num]),
  3593.         Normal);
  3594.     else
  3595.       entry = adrbk_get_entryref(ab, (a_c_arg_t)entry_num, Normal);
  3596.  
  3597.     if(entry == NULL || entry->uid_nick == NO_UID){
  3598.         dprint(1,
  3599.         (debugfile,
  3600.         "adrbk_write(%s): premature end while writing addrbook (%s)\n",
  3601.         ab->filename,
  3602.         (entry == NULL) ? "entry is NULL" : "uid_nick is NO_UID"));
  3603.         goto io_error;
  3604.     }
  3605.  
  3606.     ae = init_ae_entry(ab, entry); /* free it ourselves below */
  3607.     if(ae == (AdrBk_Entry *)NULL){
  3608.         dprint(1,
  3609.         (debugfile,
  3610.         "adrbk_write(%s): can't find ae while writing addrbook\n",
  3611.         ab->filename));
  3612.         goto io_error;
  3613.     }
  3614.  
  3615.     /*
  3616.      * This works because the information is still correct for the
  3617.      * old file.  The offsets for the old file are still the correct
  3618.      * values, the offsets that get set below are for the temporary
  3619.      * file, which will become the new file at the end.  There may
  3620.      * be some ae's that don't have offsets set yet, but they should
  3621.      * have cached ae values set if we did everything correctly.
  3622.      */
  3623.  
  3624.     /* adjust EntryRef for this entry */
  3625.     entry->uid_nick  = ab_uid(ae->nickname);
  3626.     entry->offset    = ftell(ab_stream);
  3627.     entry->ae        = (AdrBk_Entry *)NULL;
  3628.     hash             = ab_hash(ae->nickname, (a_c_arg_t)ab->htable_size);
  3629.     entry->next_nick = ab->hash_by_nick->harray[hash];
  3630.     ab->hash_by_nick->harray[hash] = entry_num;
  3631.     if(ae->tag == Single){
  3632.         entry->uid_addr  = ab_uid(ae->addr.addr);
  3633.         hash         = ab_hash(ae->addr.addr, (a_c_arg_t)ab->htable_size);
  3634.         entry->next_addr = ab->hash_by_addr->harray[hash];
  3635.         ab->hash_by_addr->harray[hash] = entry_num;
  3636.     }
  3637.     else{
  3638.         entry->uid_addr  = NO_UID;
  3639.         entry->next_addr = NO_NEXT;
  3640.     }
  3641.  
  3642.     /* this writes to the OnDisk hashtable */
  3643.     if(write_single_entryref(entry, fp_for_hash))
  3644.           goto io_error;
  3645.  
  3646.     /* write to temp file */
  3647.     if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
  3648.         &this_full_width, &this_addr_width, &this_fcc_width) == EOF)
  3649.           goto io_error;
  3650.  
  3651.     free_ae(ab, &ae);
  3652.  
  3653.     /* keep track of widths */
  3654.     max_nick = max(max_nick, this_nick_width);
  3655.     if(this_full_width > max_full){
  3656.         full_three = full_two;
  3657.         full_two   = max_full;
  3658.         max_full   = this_full_width;
  3659.     }
  3660.     else if(this_full_width > full_two){
  3661.         full_three = full_two;
  3662.         full_two   = this_full_width;
  3663.     }
  3664.     else if(this_full_width > full_three){
  3665.         full_three = this_full_width;
  3666.     }
  3667.  
  3668.     if(this_addr_width > max_addr){
  3669.         addr_three = addr_two;
  3670.         addr_two   = max_addr;
  3671.         max_addr   = this_addr_width;
  3672.     }
  3673.     else if(this_addr_width > addr_two){
  3674.         addr_three = addr_two;
  3675.         addr_two   = this_addr_width;
  3676.     }
  3677.     else if(this_addr_width > addr_three){
  3678.         addr_three = this_addr_width;
  3679.     }
  3680.  
  3681.     if(this_fcc_width > max_fcc){
  3682.         fcc_three = fcc_two;
  3683.         fcc_two   = max_fcc;
  3684.         max_fcc   = this_fcc_width;
  3685.     }
  3686.     else if(this_fcc_width > fcc_two){
  3687.         fcc_three = fcc_two;
  3688.         fcc_two   = this_fcc_width;
  3689.     }
  3690.     else if(this_fcc_width > fcc_three){
  3691.         fcc_three = this_fcc_width;
  3692.     }
  3693.  
  3694.     /*
  3695.      * Check to see if we've been interrupted.  We check at the bottom
  3696.      * of the loop so that we can handle an interrupt in the last
  3697.      * iteration.
  3698.      */
  3699.     if(ps_global->intr_pending){
  3700.         interrupt_happened++;
  3701.         goto io_error;
  3702.     }
  3703.     }
  3704.  
  3705.     intr_handling_off();
  3706.  
  3707.     if(fclose(ab_stream) == EOF)
  3708.       goto io_error;
  3709.  
  3710.     ab_stream = (FILE *)NULL;
  3711.  
  3712.     file_attrib_copy(ab->temp_filename, ab->filename);
  3713.     if(ab->fp){
  3714.     (void)fclose(ab->fp);
  3715.     ab->fp = NULL;            /* in case of problems */
  3716.     }
  3717.  
  3718.     if(rename_file(ab->temp_filename, ab->filename) < 0)
  3719.       goto io_error;
  3720.  
  3721.     /* reopen fp to new file */
  3722.     ab->fp = fopen(ab->filename, READ_MODE);
  3723.  
  3724.     progress |= AB_RENAMED_ABOOK;
  3725.  
  3726.     widths = &ab->widths;
  3727.     widths->max_nickname_width  = max_nick;
  3728.     widths->max_fullname_width  = max_full;
  3729.     widths->max_addrfield_width = max_addr;
  3730.     widths->max_fccfield_width  = max_fcc;
  3731.     widths->third_biggest_fullname_width  = full_three;
  3732.     widths->third_biggest_addrfield_width = addr_three;
  3733.     widths->third_biggest_fccfield_width  = fcc_three;
  3734.  
  3735.     /* record new change date of addrbook file */
  3736.     ab->last_change_we_know_about = get_adj_fp_file_mtime(ab->fp);
  3737.     
  3738.     if(write_hash_table(ab->hash_by_nick, fp_for_hash,
  3739.     (a_c_arg_t)ab->htable_size))
  3740.       goto io_error;
  3741.  
  3742.     if(write_hash_table(ab->hash_by_addr, fp_for_hash,
  3743.     (a_c_arg_t)ab->htable_size))
  3744.       goto io_error;
  3745.  
  3746.     if(write_hash_trailer(ab, fp_for_hash, 1))
  3747.       goto io_error;
  3748.  
  3749.     if(fclose(fp_for_hash) == EOF)
  3750.       goto io_error;
  3751.  
  3752.     fp_for_hash = (FILE *)NULL;
  3753.  
  3754.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  3755.     if(ab->fp_hash){
  3756.     (void)fclose(ab->fp_hash);
  3757.     ab->fp_hash = NULL;        /* in case there's a problem */
  3758.     }
  3759.  
  3760.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  3761.       goto io_error;
  3762.  
  3763.     progress |= AB_RENAMED_HASH;
  3764.  
  3765.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  3766.  
  3767. #ifndef    DOS
  3768.     (void)signal(SIGHUP, save_sighup);
  3769. #endif
  3770.  
  3771.     /* this clears the Locked state as well as making the cache correct */
  3772.     init_entryref_cache(ab);
  3773.     writing = 0;
  3774.     if(we_cancel)
  3775.       cancel_busy_alarm(0);
  3776.  
  3777.     return 0;
  3778.  
  3779.  
  3780. io_error:
  3781.     intr_handling_off();
  3782. #ifndef    DOS
  3783.     (void)signal(SIGHUP, save_sighup);
  3784. #endif
  3785.     if(interrupt_happened){
  3786.     q_status_message(0, 1, 2, "Interrupt!  Reverting to previous version");
  3787.     display_message('x');
  3788.     dprint(2,
  3789.         (debugfile, "adrbk_write(%s): Interrupt\n", ab->filename));
  3790.     }
  3791.     else
  3792.       dprint(2,
  3793.     (debugfile, "adrbk_write(%s): some sort of io_error\n", ab->filename));
  3794.  
  3795.     writing = 0;
  3796.     if(we_cancel)
  3797.       cancel_busy_alarm(-1);
  3798.  
  3799.     if(ae)
  3800.       free_ae(ab, &ae);
  3801.  
  3802.     if(ab_stream){
  3803.     fclose(ab_stream);
  3804.     ab_stream = (FILE *)NULL;
  3805.     }
  3806.  
  3807.     if(fp_for_hash){
  3808.     fclose(fp_for_hash);
  3809.     fp_for_hash = (FILE *)NULL;
  3810.     }
  3811.  
  3812.     if(ab && ab->temp_filename)
  3813.       unlink(ab->temp_filename);
  3814.  
  3815.     if(ab && ab->temp_hashfile)
  3816.       unlink(ab->temp_hashfile);
  3817.  
  3818.     ab->deleted_cnt = saved_deleted_cnt; /* is this necessary? */
  3819.     adrbk_partial_close(ab);
  3820.  
  3821.     /*
  3822.      * If both the address book and hash files have been replaced, this
  3823.      * means that the open failed.  This really shouldn't happen.  We don't
  3824.      * try to recover from it here.  Instead, we'll let the address book
  3825.      * trouble detection code catch it if need be.
  3826.      */
  3827.     if(progress & AB_RENAMED_HASH){
  3828.     dprint(2,
  3829.     (debugfile, "adrbk_write(%s): already renamed hashfile: %s\n",
  3830.     ab->filename, error_description(errno)));
  3831.     }
  3832.     /*
  3833.      * We're kind of in no man's land with this case.  The null fp_hash
  3834.      * should trigger an lu rebuild in adrbk_get_entryref.
  3835.      */
  3836.     else if(progress & AB_RENAMED_ABOOK){
  3837.     ab->fp_hash = (FILE *)NULL;
  3838.     free_ab_adrhash(&ab->hash_by_nick);
  3839.     free_ab_adrhash(&ab->hash_by_addr);
  3840.     }
  3841.     /*
  3842.      * We'd come here on an i/o error in the
  3843.      * main loop.  We revert to the previous version.
  3844.      */
  3845.     else{
  3846.     free_ab_adrhash(&ab->hash_by_nick);
  3847.     free_ab_adrhash(&ab->hash_by_addr);
  3848.     /* htable size is reset from file in bld... */
  3849.     (void)bld_hash_from_ondisk_hash(ab);
  3850.     }
  3851.  
  3852.     if(interrupt_happened){
  3853.     errno = EINTR;  /* for nicer error message */
  3854.     return -5;
  3855.     }
  3856.     else
  3857.       return -2;
  3858. }
  3859.  
  3860.  
  3861. /*
  3862.  * Writes one addrbook entry with wrapping.  Fills in widths.
  3863.  * Returns 0, or EOF on error.
  3864.  *
  3865.  * Continuation lines always start with spaces.  Tabs are treated as
  3866.  * separators, never as whitespace.  When we output tab separators we
  3867.  * always put them on the ends of lines, never on the start of a line
  3868.  * after a continuation.  That is, there is always something printable
  3869.  * after continuation spaces.
  3870.  */
  3871. int
  3872. write_single_abook_entry(ae, fp, ret_nick_width, ret_full_width,
  3873.              ret_addr_width, ret_fcc_width)
  3874.     AdrBk_Entry *ae;
  3875.     FILE        *fp;
  3876.     int         *ret_nick_width, *ret_full_width,
  3877.         *ret_addr_width, *ret_fcc_width;
  3878. {
  3879.     register int len;
  3880.     int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
  3881.     int tmplen, this_width;
  3882.     char *p;
  3883.  
  3884.     /* write to temp file */
  3885.     if(fputs(ae->nickname, fp) == EOF)
  3886.       return(EOF);
  3887.  
  3888.     nick_width = strlen(ae->nickname);
  3889.     putc(TAB, fp);
  3890.     len = nick_width + (TABWIDTH - nick_width % TABWIDTH);
  3891.     if(ae->fullname){
  3892.     p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3893.                             ae->fullname, NULL);
  3894.     full_width = strlen(p);
  3895.     if(p == ae->fullname)
  3896.       this_width = full_width;
  3897.     else
  3898.       this_width = strlen(ae->fullname);
  3899.  
  3900.     len += (this_width + (TABWIDTH - this_width % TABWIDTH));
  3901.     if(len > 80){
  3902.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  3903.           return(EOF);
  3904.  
  3905.         tmplen = this_width + INDENT;
  3906.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  3907.     }
  3908.  
  3909.     if(fputs(ae->fullname, fp) == EOF)
  3910.       return(EOF);
  3911.     }
  3912.     else{
  3913.     full_width = 0;
  3914.     len += TABWIDTH;
  3915.     }
  3916.  
  3917.     putc(TAB, fp);
  3918.     /* special case, make sure empty list has () */
  3919.     if(ae->tag == List && ae->addr.list == NULL){
  3920.     addr_width = 0;
  3921.     putc('(', fp);
  3922.     putc(')', fp);
  3923.     }
  3924.     else if(ae->addr.addr != NULL || ae->addr.list != NULL){
  3925.     if(ae->tag == Single){
  3926.         /*----- Single: just one address ----*/
  3927.         p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3928.                             ae->addr.addr, NULL);
  3929.         addr_width = strlen(p);
  3930.         if(p == ae->addr.addr)
  3931.           this_width = addr_width;
  3932.         else
  3933.           this_width = strlen(ae->addr.addr);
  3934.  
  3935.         len += (this_width + (TABWIDTH - this_width % TABWIDTH));
  3936.         if(len > 80){
  3937.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  3938.           return(EOF);
  3939.  
  3940.         tmplen = this_width + INDENT;
  3941.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  3942.         }
  3943.  
  3944.         if(fputs(ae->addr.addr, fp) == EOF)
  3945.           return(EOF);
  3946.     }
  3947.     else if(ae->tag == List){
  3948.         register char **a2;
  3949.  
  3950.         /*----- List: a distribution list ------*/
  3951.         putc('(', fp);
  3952.         len++;
  3953.         addr_width = 0;
  3954.         for(a2 = ae->addr.list; *a2 != NULL; a2++){
  3955.         if(a2 != ae->addr.list){
  3956.             putc(',', fp);
  3957.             len++;
  3958.         }
  3959.  
  3960.         /*
  3961.          * comma or ) also follows, so we're breaking at
  3962.          * no more than 79 chars
  3963.          */
  3964.         p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  3965.                         *a2, NULL);
  3966.         tmplen = strlen(p);
  3967.         if(p == *a2)
  3968.           this_width = tmplen;
  3969.         else
  3970.           this_width = strlen(*a2);
  3971.  
  3972.         addr_width = max(addr_width, tmplen);
  3973.         if(len + this_width > 78 && len != INDENT){
  3974.             /*--- break up long lines ----*/
  3975.             if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  3976.               return(EOF);
  3977.  
  3978.             len = INDENT;
  3979.         }
  3980.  
  3981.         if(fputs(*a2, fp) == EOF)
  3982.           return(EOF);
  3983.  
  3984.         len += this_width;
  3985.         }
  3986.  
  3987.         putc(')', fp);
  3988.     }
  3989.     }
  3990.  
  3991.     /* If either fcc or extra exists, output both, otherwise, neither */
  3992.     if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
  3993.     putc(TAB, fp);
  3994.     len += (TABWIDTH - len % TABWIDTH);
  3995.     if(ae->fcc && ae->fcc[0]){
  3996.         fcc_width = strlen(ae->fcc);
  3997.         len += (fcc_width + (TABWIDTH - fcc_width%TABWIDTH));
  3998.         if(len > 80){
  3999.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4000.           return(EOF);
  4001.  
  4002.         tmplen = fcc_width + INDENT;
  4003.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4004.         }
  4005.  
  4006.         if(fputs(ae->fcc, fp) == EOF)
  4007.           return(EOF);
  4008.     }
  4009.     else
  4010.       fcc_width = 0;
  4011.  
  4012.     putc(TAB, fp);
  4013.     if(ae->extra && ae->extra[0]){
  4014.         tmplen = strlen(ae->extra);
  4015.         len += (tmplen + (TABWIDTH - tmplen % TABWIDTH));
  4016.         if(len > 80){
  4017.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4018.           return(EOF);
  4019.  
  4020.         tmplen += INDENT;
  4021.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4022.         }
  4023.  
  4024.         if(fputs(ae->extra, fp) == EOF)
  4025.           return(EOF);
  4026.     }
  4027.     }
  4028.     else
  4029.       fcc_width = 0;
  4030.  
  4031.     putc('\n', fp);
  4032.     
  4033.     if(ret_nick_width)
  4034.       *ret_nick_width = nick_width;
  4035.     if(ret_full_width)
  4036.       *ret_full_width = full_width;
  4037.     if(ret_addr_width)
  4038.       *ret_addr_width = addr_width;
  4039.     if(ret_fcc_width)
  4040.       *ret_fcc_width = fcc_width;
  4041.  
  4042.     return(0);
  4043. }
  4044.  
  4045.  
  4046. int
  4047. percent_abook_saved()
  4048. {
  4049.     return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
  4050.     (unsigned long)tot_for_percent));
  4051. }
  4052.  
  4053.  
  4054. /*
  4055.  * Free memory associated with entry ae.
  4056.  *
  4057.  * Args:  ab  -- Address book
  4058.  *        ae  -- Address book entry to be freed.
  4059.  */
  4060. void
  4061. free_ae(ab, ae)
  4062.     AdrBk        *ab;
  4063.     AdrBk_Entry **ae;
  4064. {
  4065.     char **p;
  4066.  
  4067.     if(!ab || !ae || !(*ae))
  4068.       return;
  4069.  
  4070.     if((*ae)->nickname && (*ae)->nickname != empty)
  4071.       fs_give((void **)&(*ae)->nickname);
  4072.  
  4073.     if((*ae)->fullname && (*ae)->fullname != empty)
  4074.       fs_give((void **)&(*ae)->fullname);
  4075.  
  4076.     if((*ae)->tag == Single){
  4077.         if((*ae)->addr.addr && (*ae)->addr.addr != empty)
  4078.           fs_give((void **)&(*ae)->addr.addr);
  4079.     }
  4080.     else if((*ae)->tag == List){
  4081.         if((*ae)->addr.list){
  4082.             for(p = (*ae)->addr.list; *p; p++) 
  4083.               if(*p != empty)
  4084.             fs_give((void **)p);
  4085.  
  4086.             fs_give((void **)&(*ae)->addr.list);
  4087.         }
  4088.     }
  4089.  
  4090.     if((*ae)->fcc && (*ae)->fcc != empty)
  4091.       fs_give((void **)&(*ae)->fcc);
  4092.  
  4093.     if((*ae)->extra && (*ae)->extra != empty)
  4094.       fs_give((void **)&(*ae)->extra);
  4095.  
  4096.     fs_give((void **)ae);
  4097. }
  4098.  
  4099.  
  4100. /*
  4101.  * Writes the Header part of on-disk hash table.
  4102.  * Returns 0 on success, -1 on failure.
  4103.  */
  4104. int
  4105. write_hash_header(fp, size)
  4106.     FILE        *fp;
  4107.     a_c_arg_t    size;
  4108. {
  4109.     dprint(9, (debugfile, "- write_hash_header -\n"));
  4110.  
  4111.     if(fp == (FILE *)NULL)
  4112.       return -1;
  4113.  
  4114.     /* 10 is SIZEOF_HASH_SIZE */
  4115. #ifdef HUGE_ADDRBOOKS
  4116.     if(fprintf(fp, "%s %s %10lu\n", PMAGIC, ADRHASH_FILE_VERSION_NUM,
  4117.     (unsigned long)size) == EOF)
  4118. #else /* !HUGE_ADDRBOOKS */
  4119.     if(fprintf(fp, "%s %s %5u\n", PMAGIC, ADRHASH_FILE_VERSION_NUM,
  4120.     (unsigned)size) == EOF)
  4121. #endif /* !HUGE_ADDRBOOKS */
  4122.     return -1;
  4123.     
  4124.     return 0;
  4125. }
  4126.  
  4127.  
  4128. int
  4129. write_single_entryref(entry, fp)
  4130.     EntryRef *entry;
  4131.     FILE    *fp;
  4132. {
  4133.     if(fp == (FILE *)NULL)
  4134.       return -1;
  4135.  
  4136.     if(entry == (EntryRef *)NULL || entry->uid_nick == NO_UID)
  4137.       return -1;
  4138.  
  4139.     /*
  4140.      * The first two widths below should be the same as SIZEOF_HASH_INDEX.
  4141.      * The next two the same as SIZEOF_UID.
  4142.      * The last one the same as SIZEOF_FILEOFFSET.
  4143.      */
  4144. #ifdef HUGE_ADDRBOOKS
  4145.     if(fprintf(fp, "%10lu %10lu %11ld %11ld %10ld\n",
  4146.         (unsigned long)entry->next_nick,
  4147.         (unsigned long)entry->next_addr,
  4148.         (long)entry->uid_nick,
  4149.         (long)entry->uid_addr,
  4150.         (long)entry->offset) == EOF)
  4151. #else /* !HUGE_ADDRBOOKS */
  4152.     if(fprintf(fp, "%5u %5u %11ld %11ld %10ld\n",
  4153.         (unsigned)entry->next_nick,
  4154.         (unsigned)entry->next_addr,
  4155.         (long)entry->uid_nick,
  4156.         (long)entry->uid_addr,
  4157.         (long)entry->offset) == EOF)
  4158. #endif /* !HUGE_ADDRBOOKS */
  4159.     return -1;
  4160.  
  4161.     return 0;
  4162. }
  4163.  
  4164.  
  4165. /*
  4166.  * Writes the HashTable part of on-disk hash table.
  4167.  * Returns 0 on success, -1 on failure.
  4168.  */
  4169. int
  4170. write_hash_table(ahash, fp, size_arg)
  4171.     AdrHash *ahash;
  4172.     FILE    *fp;
  4173.     a_c_arg_t size_arg;
  4174. {
  4175.     adrbk_cntr_t i;
  4176.     adrbk_cntr_t size;
  4177.  
  4178.     dprint(9, (debugfile, "- write_hash_table -\n"));
  4179.  
  4180.     size = (adrbk_cntr_t)size_arg;
  4181.  
  4182.     if(fp == (FILE *)NULL)
  4183.       return -1;
  4184.  
  4185.     /*
  4186.      * The width below should be the same as SIZEOF_HASH_INDEX.
  4187.      */
  4188.     for(i = 0; i < size; i++){
  4189. #ifdef HUGE_ADDRBOOKS
  4190.     if(fprintf(fp, "%10lu\n", (unsigned long)(ahash->harray[i])) == EOF)
  4191. #else /* !HUGE_ADDRBOOKS */
  4192.     if(fprintf(fp, "%5u\n", (unsigned)(ahash->harray[i])) == EOF)
  4193. #endif /* !HUGE_ADDRBOOKS */
  4194.         return -1;
  4195.     }
  4196.  
  4197.     return 0;
  4198. }
  4199.  
  4200.  
  4201. /*
  4202.  * Writes the Trailer part of on-disk hash table.
  4203.  * Returns 0 on success, -1 on failure.
  4204.  */
  4205. int
  4206. write_hash_trailer(ab, fp, know_its_sorted)
  4207.     AdrBk *ab;
  4208.     FILE  *fp;
  4209.     int    know_its_sorted;
  4210. {
  4211.     WIDTH_INFO_S *widths;
  4212.  
  4213.     dprint(9, (debugfile, "- write_hash_trailer -\n"));
  4214.  
  4215.     if(fp == (FILE *)NULL)
  4216.       return -1;
  4217.  
  4218.     /*
  4219.      * The width below should be the same as SIZEOF_COUNT.
  4220.      */
  4221. #ifdef HUGE_ADDRBOOKS
  4222.     if(fprintf(fp, "%s %10lu\n", PMAGIC, (unsigned long)(ab->count)) == EOF)
  4223. #else /* !HUGE_ADDRBOOKS */
  4224.     if(fprintf(fp, "%s %5u\n", PMAGIC, (unsigned)(ab->count)) == EOF)
  4225. #endif /* !HUGE_ADDRBOOKS */
  4226.       return -1;
  4227.     
  4228.     if(fprintf(fp, "%11ld\n", ab->deleted_cnt) == EOF)
  4229.       return -1;
  4230.  
  4231.     widths = &ab->widths;
  4232.     /*
  4233.      * The 2's are the same as SIZEOF_WIDTH.
  4234.      */
  4235.     if(fprintf(fp, " %2d %2d %2d %2d %2d %2d %2d\n",
  4236.         widths->max_nickname_width,
  4237.         widths->max_fullname_width,
  4238.         widths->max_addrfield_width,
  4239.         widths->max_fccfield_width,
  4240.         widths->third_biggest_fullname_width,
  4241.         widths->third_biggest_addrfield_width,
  4242.         widths->third_biggest_fccfield_width) == EOF)
  4243.     return -1;
  4244.  
  4245. #define SLOP 3  /* add to make sure date is later than addrbook file date */
  4246.     /*
  4247.      * 10 is SIZEOF_TIMESTAMP.
  4248.      * 2 is SIZEOF_SORT_RULE.
  4249.      */
  4250.     if(fprintf(fp, "%10lu %2d\n", (unsigned long)get_adj_time() + SLOP,
  4251.     know_its_sorted ? ab->sort_rule : -1) == EOF)
  4252.     return -1;
  4253.     
  4254.     return 0;
  4255. }
  4256.  
  4257.  
  4258. /*
  4259.  * Use this size hashtables for count addrbook entries.
  4260.  */
  4261. adrbk_cntr_t
  4262. hashtable_size(count)
  4263.     a_c_arg_t count;
  4264. {
  4265.     long size;
  4266.  
  4267.     /*
  4268.      * These are picked almost totally out of the blue.  Higher values cause
  4269.      * larger hashtables so longer time to write and seek through them on
  4270.      * disk (and more diskspace).  Higher also implies fewer entries per
  4271.      * hash bucket so fewer seeks through the file.  Higher is probably
  4272.      * better if we can afford it.
  4273.      */
  4274.     if(count < 100)
  4275.       size = 100L;
  4276.     else if(count < 300)
  4277.       size = 300L;
  4278.     else if(count < 600)
  4279.       size = 600L;
  4280.     else if(count < 1000)
  4281.       size = 1000L;
  4282.     else if(count < 4000)
  4283.       size = 2000L;
  4284.     else if(count < 10000)
  4285.       size = 5000L;
  4286.     else if(count < 20000)
  4287.       size = 10000L;
  4288.     else if(count < 40000)
  4289.       size = 20000L;
  4290.     else if(count < 80000)
  4291.       size = 40000L;
  4292.     else if(count < 150000)
  4293.       size = 70000L;
  4294.     else if(count < 300000)
  4295.       size = 120000L;
  4296.     else
  4297.       size = 150000L;
  4298.     
  4299.     if(size > MAX_HASHTABLE_SIZE)
  4300.       size = MAX_HASHTABLE_SIZE;
  4301.  
  4302.     return((adrbk_cntr_t)size);
  4303. }
  4304.  
  4305.  
  4306. /*
  4307.  * This manages a cache of entryrefs and their corresponding addrbook
  4308.  * entries.  The addrbook entries could be split off to either be cached
  4309.  * or not separately, but we aren't doing that now.  It is possible for an
  4310.  * entryref to be cached without its addrbook entry being instantiated, but
  4311.  * if the cached entryref does have an addrbook entry to go with it, then
  4312.  * that cached entry is freed when the entryref is removed from the cache.
  4313.  * The virtual entryref array has ab->count entries, so may be too big to keep
  4314.  * in memory.  The current caching is LRU, except for the locked entries.
  4315.  * If the referenced flag is set in the
  4316.  * associated addrbook entry, then we leave the entry in the cache.  That's
  4317.  * because the referenced flag is not stored when it is removed from the
  4318.  * cache.  If the handling==Lock, we also leave that entry in the cache.
  4319.  *
  4320.  * Handling: Normal - Will be cached by the cache mgr (this function) and may
  4321.  *                    disappear when subsequent calls to the mgr are made.
  4322.  *           Delete - Will be removed from cache if there, and will be marked
  4323.  *                    as deleted so adrbk_write will skip it.
  4324.  *       SaveDelete - Will be removed from cache if there, and will be marked
  4325.  *                    as deleted so adrbk_write will skip it.  Will also be
  4326.  *                    saved in file as #DELETED- entry.
  4327.  *            Lock  - Tells the mgr to lock it in cache until told it is ok
  4328.  *                    to release from cache.  A call with handling set to
  4329.  *                    Unlock tells the mgr it is ok to release.  Actually, a
  4330.  *                    lock_ref_count is kept so that nested locks will work
  4331.  *                    properly.  Each call to Lock increments the ref count
  4332.  *                    by one and each call to Unlock decrements.  The Unlock
  4333.  *                    only changes handling to Normal when ref count reaches 0.
  4334.  *           Unlock - See Lock description.
  4335.  */
  4336. EntryRef *
  4337. adrbk_get_entryref(ab, elem_arg, handling)
  4338.     AdrBk       *ab;
  4339.     a_c_arg_t    elem_arg;
  4340.     Handling     handling;
  4341. {
  4342.     adrbk_cntr_t elem;
  4343.     EntryRef *return_entry = (EntryRef *)NULL;
  4344.     EntryRef *new_e = (EntryRef *)NULL;
  4345.     char *p;
  4346.     char line[MAXLINE+1];
  4347.     register ER_CACHE_ELEM_S *cptr;
  4348.     adrbk_cntr_t hash_bucket;
  4349.     ER_CACHE_ELEM_S *new_cache_element, *old_first_entry;
  4350.     ER_CACHE_ELEM_S *deleted;
  4351.     ER_CACHE_ELEM_S *head, *tail;
  4352.     ER_CACHE_ELEM_S *preceding_elem, *following_elem, *previous_top_dog;
  4353.     AdrBk_Entry *ae;
  4354.     int entryref_returned_null = 0;
  4355.  
  4356.     elem = (adrbk_cntr_t)elem_arg;
  4357.  
  4358.     dprint(9, (debugfile,
  4359.     "adrbk_get_entryref(%s) - elem=%lu (%s)\n",
  4360.     ab->filename,
  4361.     (unsigned long)elem,
  4362.     handling==Normal ? "Normal" :
  4363.      handling==Delete ? "Delete" :
  4364.       handling==SaveDelete ? "SaveDelete" :
  4365.        handling==Lock ? "Lock" :
  4366.         handling==Unlock ? "Unlock" :
  4367.                 "Unknown"));
  4368.  
  4369.     if(!ab || !ab->fp_hash || elem >= ab->count)
  4370.       goto big_trouble;
  4371.     
  4372.     /*
  4373.      * Deleted_elem just marks that element as deleted, but it still shows
  4374.      * up there.  So, we have to skip over it.  The caller won't know about
  4375.      * deleted_elem, so the caller will be using element numbers as if
  4376.      * deleted wasn't there.  Adjust for that.  This is only used for
  4377.      * short times before we rewrite the adrbk to disk with adrbk_write.
  4378.      * It gives us a way to delete an entry.
  4379.      *  0  1  D  3  4  5  Looks like this to us, deleted_elem = 2.
  4380.      *  0  1     2  3  4  Treat like this.  Note, count = 5, not 6.
  4381.      *
  4382.      * insert_before is like deleted_elem, but the opposite.  If insert_before
  4383.      * is 5, that means it logically belongs before elem number 5.  Have to
  4384.      * adjust element numbers to reflect that.
  4385.      *  0  1  2  3  4  5
  4386.      *             ^
  4387.      *             insert_before = 4.
  4388.      *  0  1  2  3  5  6
  4389.      *             ^
  4390.      *             inserted_entryref goes here when you ask for 4.
  4391.      *
  4392.      * Can't have both but there is also moved_elem to move a single element
  4393.      * from one place to another.
  4394.      *  0  1  2  3  4  5  6
  4395.      *       ^         ^  moved_elem = 5, move_elem_before = 2
  4396.      *  0  1  5  2  3  4  6
  4397.      *
  4398.      *       or
  4399.      *
  4400.      *  0  1  2  3  4  5  6
  4401.      *        ^       ^   moved_elem = 2, move_elem_before = 5
  4402.      *  0  1  3  4  2  5  6
  4403.      *
  4404.      *       or          special case, move_elem_before = count
  4405.      *
  4406.      *  0  1  2  3  4  5  6
  4407.      *        ^             ^  moved_elem = 2, move_elem_before = 7
  4408.      *  0  1  3  4  5  6  2
  4409.      *
  4410.      * Can only use one of these mechanisms at a time.  The first "if" is
  4411.      * a test that at most one is being used.
  4412.      */
  4413.     if((((deleted_elem == NO_NEXT) ? 1 : 0) +
  4414.        ((moved_elem    == NO_NEXT) ? 1 : 0) +
  4415.        ((insert_before == NO_NEXT) ? 1 : 0)) < 2){
  4416.     panic("Programming botch in adrbk_get_entryref()");
  4417.     }
  4418.     else if(deleted_elem != NO_NEXT){
  4419.     if(elem >= deleted_elem)
  4420.       elem++;
  4421.     }
  4422.     else if(insert_before != NO_NEXT){
  4423.     if(elem == insert_before)
  4424.       return(&inserted_entryref);
  4425.  
  4426.     if(elem > insert_before)
  4427.       elem--;
  4428.     }
  4429.     else if(moved_elem != NO_NEXT){
  4430.     adrbk_cntr_t el_small, el_large;
  4431.  
  4432.     el_small = min(moved_elem, move_elem_before);
  4433.     el_large = max(moved_elem, move_elem_before);
  4434.  
  4435.     if(elem < el_small ||
  4436.        elem > el_large ||
  4437.        moved_elem == move_elem_before ||
  4438.        moved_elem == move_elem_before - 1){
  4439.         /* no change */
  4440.         }
  4441.     else if(moved_elem > move_elem_before){
  4442.         if(elem == move_elem_before)
  4443.           elem = moved_elem;
  4444.         else
  4445.           elem--;
  4446.     }
  4447.     else{ /* moved_elem < move_elem_before */
  4448.         if(elem == move_elem_before - 1)
  4449.           elem = moved_elem;
  4450.         else if(elem < move_elem_before)
  4451.           elem++;
  4452.     }
  4453.     }
  4454.  
  4455.  
  4456.     if(handling == Delete || handling == SaveDelete){
  4457.     /*
  4458.      * Before we continue, we first want to get the deleted entry
  4459.      * and store it for use in adrbk_write.
  4460.      */
  4461.     if(handling == SaveDelete){
  4462.         ae = adrbk_get_ae(ab, elem_arg, Normal);
  4463.         if(ae)
  4464.           deleted_ae = copy_ae(ae);
  4465.     }
  4466.  
  4467.     dprint(2, (debugfile,
  4468.        "adrbk_get_entryref: entry marked deleted, count was %lu, now %lu\n",
  4469.        (unsigned long)ab->count, (unsigned long)(ab->count - 1)));
  4470.     ab->count--;
  4471.     deleted_elem = elem;
  4472.     }
  4473.  
  4474.     hash_bucket = elem % ab->er_hashsize;
  4475.  
  4476.     head = ab->head_cache_elem[hash_bucket];
  4477.     tail = ab->tail_cache_elem[hash_bucket];
  4478.  
  4479.     /* Look for element number elem in cache */
  4480.     cptr = head;
  4481.     while(cptr && cptr->elem != elem)
  4482.       cptr = cptr->next;
  4483.  
  4484.     if(cptr){  /* it was in cache */
  4485.  
  4486.     if(handling == Delete || handling == SaveDelete){
  4487.         dprint(9, (debugfile, "deleting %u from cache\n", elem));
  4488.         deleted              = cptr;
  4489.         preceding_elem       = deleted->prev;
  4490.         following_elem       = deleted->next;
  4491.         if(preceding_elem)
  4492.           preceding_elem->next = following_elem;
  4493.         else{
  4494.         ab->head_cache_elem[hash_bucket] = following_elem;
  4495.         head = ab->head_cache_elem[hash_bucket];
  4496.         }
  4497.  
  4498.         if(following_elem)
  4499.           following_elem->prev = preceding_elem;
  4500.         else{
  4501.         ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4502.         tail = ab->tail_cache_elem[hash_bucket];
  4503.         }
  4504.  
  4505.         free_ab_entryref(ab, deleted->entry);
  4506.         ab->n_ae_cached_in_this_bucket[hash_bucket]--;
  4507.         fs_give((void **)&deleted);
  4508.         return((EntryRef *)NULL);
  4509.     }
  4510.     else{
  4511.         if(handling == Unlock){
  4512.         cptr->lock_ref_count--;
  4513.         if(cptr->lock_ref_count <= 0)
  4514.           cptr->handling = Normal;
  4515.         }
  4516.         else if(handling == Lock){
  4517.         cptr->handling = Lock;
  4518.         cptr->lock_ref_count++;
  4519.         }
  4520.  
  4521.         return_entry = cptr->entry;
  4522.     }
  4523.  
  4524.     /*
  4525.      * Put this entry back at head of cache so that we remain LRU (within
  4526.      * each hash bucket).
  4527.      */
  4528.      if(cptr->prev != (ER_CACHE_ELEM_S *)NULL){
  4529.         preceding_elem         = cptr->prev;
  4530.         following_elem         = cptr->next;
  4531.         previous_top_dog       = head;
  4532.  
  4533.         preceding_elem->next   = following_elem;
  4534.         if(following_elem)
  4535.           following_elem->prev = preceding_elem;
  4536.         else
  4537.           ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4538.  
  4539.         ab->head_cache_elem[hash_bucket] = cptr;
  4540.         previous_top_dog->prev = cptr;
  4541.         cptr->next             = previous_top_dog;
  4542.         cptr->prev             = (ER_CACHE_ELEM_S *)NULL;
  4543.      }
  4544.     }
  4545.     else{  /* it was not in cache */
  4546.       if(handling != Delete && handling != SaveDelete){
  4547.     p = get_entryref_line_from_disk(ab->fp_hash, line, (a_c_arg_t)elem);
  4548.  
  4549.     if(p){
  4550.  
  4551.         /* fill in entryref from disk */
  4552.  
  4553.         new_e = new_entryref(NO_UID, NO_UID, -1L);
  4554.         new_e->next_nick = (adrbk_cntr_t)strtoul(p, (char **)NULL, 10);
  4555.         p += (SIZEOF_HASH_INDEX + SIZEOF_SPACE);
  4556.         new_e->next_addr = (adrbk_cntr_t)strtoul(p, (char **)NULL, 10);
  4557.         p += (SIZEOF_HASH_INDEX + SIZEOF_SPACE);
  4558.         new_e->uid_nick  = (adrbk_uid_t)atol(p);
  4559.         p += (SIZEOF_UID + SIZEOF_SPACE);
  4560.         new_e->uid_addr  = (adrbk_uid_t)atol(p);
  4561.         p += (SIZEOF_UID + SIZEOF_SPACE);
  4562.         new_e->offset    = atol(p);
  4563.         new_e->ae        = (AdrBk_Entry *)NULL;
  4564.  
  4565.         new_cache_element = (ER_CACHE_ELEM_S *)NULL;
  4566.  
  4567.         /* Remove some old cache entries */
  4568.         if(ab->n_ae_cached_in_this_bucket[hash_bucket] >=
  4569.         CACHE_PER_ER_BUCKET){
  4570.  
  4571.         /*
  4572.          * Only delete if hasn't been referenced, for loop detect.
  4573.          * This is because the reference count will go away if we
  4574.          * delete it from the cache.
  4575.          *
  4576.          * Don't delete Locked entries.
  4577.          *
  4578.          * Find entries we can delete until we get down to the
  4579.          * number we want in cache.
  4580.          */
  4581.         for(cptr = tail; cptr; cptr = cptr->prev){
  4582.  
  4583.             if(cptr->handling == Lock)
  4584.               continue;
  4585.  
  4586.             if(cptr->entry && cptr->entry->ae){
  4587.             ae = cptr->entry->ae;
  4588.             if(ae->referenced != 0)
  4589.                 continue;
  4590.             }
  4591.  
  4592.             deleted              = cptr;
  4593.             preceding_elem       = deleted->prev;
  4594.             following_elem       = deleted->next;
  4595.             if(preceding_elem)
  4596.               preceding_elem->next = following_elem;
  4597.             else{
  4598.             ab->head_cache_elem[hash_bucket] = following_elem;
  4599.             head = ab->head_cache_elem[hash_bucket];
  4600.             }
  4601.  
  4602.             if(following_elem)
  4603.               following_elem->prev = preceding_elem;
  4604.             else{
  4605.             ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4606.             tail = ab->tail_cache_elem[hash_bucket];
  4607.             }
  4608.  
  4609.             free_ab_entryref(ab, deleted->entry);
  4610.             ab->n_ae_cached_in_this_bucket[hash_bucket]--;
  4611.             if(ab->n_ae_cached_in_this_bucket[hash_bucket] >=
  4612.             CACHE_PER_ER_BUCKET){
  4613.             fs_give((void **)&deleted);
  4614.             }
  4615.             else{
  4616.             new_cache_element = deleted;
  4617.             break;
  4618.             }
  4619.         }
  4620.         }
  4621.  
  4622.         if(new_cache_element == (ER_CACHE_ELEM_S *)NULL)
  4623.         new_cache_element =
  4624.             (ER_CACHE_ELEM_S *)fs_get(sizeof(ER_CACHE_ELEM_S));
  4625.  
  4626.         /* insert new entry at head of cache */
  4627.  
  4628.         /*
  4629.          * Unlock happening here would be a mistake, because Unlock should
  4630.          * only be called after a Lock, and a Locked entry would have
  4631.          * been in the cache.  But no harm in making it act just like
  4632.          * a Normal.
  4633.          */
  4634.         new_cache_element->lock_ref_count = 0;
  4635.         if(handling == Normal || handling == Unlock)
  4636.           new_cache_element->handling = Normal;
  4637.         else if(handling == Lock){
  4638.         new_cache_element->handling = Lock;
  4639.         new_cache_element->lock_ref_count++;
  4640.         }
  4641.  
  4642.         new_cache_element->elem     = elem;
  4643.         new_cache_element->entry    = new_e;
  4644.         old_first_entry             = head;
  4645.         ab->head_cache_elem[hash_bucket] = new_cache_element;
  4646.         if(old_first_entry)
  4647.           old_first_entry->prev       = new_cache_element;
  4648.         else
  4649.           ab->tail_cache_elem[hash_bucket] = new_cache_element;
  4650.  
  4651.         new_cache_element->next     = old_first_entry;
  4652.         new_cache_element->prev     = (ER_CACHE_ELEM_S *)NULL;
  4653.         ab->n_ae_cached_in_this_bucket[hash_bucket]++;
  4654.  
  4655.         return_entry = new_e;
  4656.     }
  4657.     else{
  4658.         /*
  4659.          * This shouldn't happen.  It is possible that it might happen
  4660.          * if we get a stale NFS handle, in which case we want to reset
  4661.          * the addrbook and retry the open.
  4662.          */
  4663.         entryref_returned_null = 1;
  4664.     }
  4665.       }
  4666.     }
  4667.  
  4668. big_trouble:
  4669.     if(return_entry == (EntryRef *)NULL){
  4670.     if(!ab){
  4671.         dprint(1, (debugfile,
  4672.           "adrbk_get_entryref: ab is NULL, should not happen.\n"));
  4673.     }
  4674.     else if(!ab->fp_hash){
  4675.         dprint(1, (debugfile,
  4676.           "adrbk_get_entryref: ab->fp_hash is NULL, should not happen.\n"));
  4677.     }
  4678.     else if(elem >= ab->count){
  4679.       dprint(1, (debugfile,
  4680.      "adrbk_get_entryref: elem >= ab->count (%ld >= %ld), should not happen.\n",
  4681.      (long)elem, (long)ab->count));
  4682.     }
  4683.     else if(entryref_returned_null){
  4684.         dprint(1, (debugfile,
  4685.       "\n\n ADDR    ::: the addressbook lookup file %s\n",
  4686.           ab->hashfile ? ab->hashfile : "?"));
  4687.         dprint(1, (debugfile,
  4688.       " BOOK    ::: seems to be unreadable.  The lookup\n"));
  4689.         dprint(1, (debugfile,
  4690.       " TROUBLE ::: file may have to be removed and rebuilt.\n"));
  4691.         dprint(1, (debugfile,
  4692.      "         ::: Usually it will fix itself, but if it doesn't, or if it\n"));
  4693.         dprint(1, (debugfile,
  4694.       "         ::: is building temporary lookup files for each user,\n"));
  4695.         dprint(1, (debugfile,
  4696.       "         ::: the sys admin should rebuild it (%s).\n\n",
  4697.           ab->hashfile ? ab->hashfile : "?"));
  4698.     }
  4699.     else{
  4700.         dprint(1, (debugfile,
  4701.           "adrbk_get_entryref: returned NULL\n"));
  4702.     }
  4703.  
  4704.     dprint(1, (debugfile,
  4705.       "There must have been a problem opening or closing or something.\n"));
  4706.  
  4707.     q_status_message1(SM_ORDER | SM_DING, 5, 5, "Addrbook problems%s",
  4708.               (trouble_rebuilds<MAX_TROUBLE_REBUILDS)
  4709.                 ? ", will attempt to resync..." : "");
  4710.  
  4711.     if(trouble_rebuilds < MAX_TROUBLE_REBUILDS){
  4712.         dprint(1, (debugfile,
  4713.           "Will attempt to longjmp to safe place and try again.\n"));
  4714.  
  4715.         if(writing){
  4716.            writing = 0;
  4717.            q_status_message(SM_ORDER, 3, 5,
  4718.            "Aborting our change to avoid damage...");
  4719.         }
  4720.  
  4721.         trouble_rebuilds++;
  4722.         /* jump back to a safe place */
  4723.         trouble_filename = (ab && ab->orig_filename)
  4724.                 ? cpystr(ab->orig_filename)
  4725.                 : cpystr("");
  4726.         longjmp(addrbook_changed_unexpectedly, 1);
  4727.         /*NOTREACHED*/
  4728.     }
  4729.     }
  4730.  
  4731.     return(return_entry);
  4732. }
  4733.  
  4734.  
  4735. /*
  4736.  * It is safe to set this higher than the number of entries in the
  4737.  * addrbook, in the sense that it will only use the number of entries.  It
  4738.  * will, however, use up lots of memory if that number is big.
  4739.  */
  4740. long
  4741. adrbk_set_nominal_cachesize(ab, new_size)
  4742.     AdrBk *ab;
  4743.     long   new_size;
  4744. {
  4745.     long old_size;
  4746.  
  4747.     old_size = ab->nominal_max_cached;
  4748.  
  4749.     dprint(9, (debugfile, "- adrbk_set_nominal_cachesize - was %ld now %ld\n",
  4750.     old_size, new_size));
  4751.  
  4752.     ab->nominal_max_cached = new_size;
  4753.  
  4754.     init_entryref_cache(ab);
  4755.  
  4756.     return(old_size);
  4757. }
  4758.  
  4759.  
  4760. long
  4761. adrbk_get_nominal_cachesize(ab)
  4762.     AdrBk *ab;
  4763. {
  4764.     return(ab->nominal_max_cached);
  4765. }
  4766.  
  4767.  
  4768. /*
  4769.  * Adds a new entryref which points to new_ae before put_it_before_this.
  4770.  */
  4771. void
  4772. set_inserted_entryref(ab, put_it_before_this, new_ae)
  4773.     AdrBk *ab;
  4774.     a_c_arg_t put_it_before_this;
  4775.     AdrBk_Entry *new_ae;
  4776. {
  4777.     dprint(2, (debugfile,
  4778.        "adrbk_add: entry marked inserted, count was %lu, now %lu\n",
  4779.        (unsigned long)ab->count, (unsigned long)(ab->count + 1)));
  4780.  
  4781.     ab->count++;
  4782.     insert_before              = (adrbk_cntr_t)put_it_before_this;
  4783.     inserted_entryref.uid_nick = !NO_UID;
  4784.     inserted_entryref.ae       = new_ae;
  4785. }
  4786.  
  4787. /*
  4788.  * Moves element move_this_one before element put_it_before_this.
  4789.  */
  4790. void
  4791. set_moved_entryref(move_this_one, put_it_before_this)
  4792.     a_c_arg_t move_this_one;
  4793.     a_c_arg_t put_it_before_this;
  4794. {
  4795.     dprint(7, (debugfile, "- set_moved_entryref -\n"));
  4796.  
  4797.     moved_elem       = (adrbk_cntr_t)move_this_one;
  4798.     move_elem_before = (adrbk_cntr_t)put_it_before_this;
  4799. }
  4800.  
  4801.  
  4802. void
  4803. init_entryref_cache(ab)
  4804.     AdrBk *ab;
  4805. {
  4806.     adrbk_cntr_t i;
  4807.     adrbk_cntr_t new_hashsize;
  4808.  
  4809.     dprint(9, (debugfile, "- init_entryref_cache -\n"));
  4810.  
  4811.     if(ab->er_hashsize != 0) /* an indication we've init'd before */
  4812.       clear_entryref_cache(ab);
  4813.  
  4814.     new_hashsize = min((adrbk_cntr_t)(ab->nominal_max_cached /
  4815.                             CACHE_PER_ER_BUCKET),
  4816.                (adrbk_cntr_t)30000);
  4817.  
  4818.     if(new_hashsize == 0)
  4819.       new_hashsize = 1;
  4820.  
  4821.     if(new_hashsize != ab->er_hashsize){
  4822.  
  4823.     ab->er_hashsize = new_hashsize;
  4824.  
  4825.     /* hash array of head pointers */
  4826.     if(ab->head_cache_elem)
  4827.       fs_give((void **)&ab->head_cache_elem);
  4828.  
  4829.     if(ab->tail_cache_elem)
  4830.       fs_give((void **)&ab->tail_cache_elem);
  4831.  
  4832.     if(ab->n_ae_cached_in_this_bucket)
  4833.       fs_give((void **)&ab->n_ae_cached_in_this_bucket);
  4834.  
  4835.     ab->head_cache_elem =
  4836.       (ER_CACHE_ELEM_S **)fs_get((size_t)ab->er_hashsize *
  4837.                       sizeof(ER_CACHE_ELEM_S *));
  4838.     ab->tail_cache_elem =
  4839.       (ER_CACHE_ELEM_S **)fs_get((size_t)ab->er_hashsize *
  4840.                       sizeof(ER_CACHE_ELEM_S *));
  4841.     ab->n_ae_cached_in_this_bucket =
  4842.       (int *)fs_get((size_t)ab->er_hashsize * sizeof(int));
  4843.     memset(ab->n_ae_cached_in_this_bucket, 0,
  4844.         (size_t)ab->er_hashsize * sizeof(int));
  4845.     }
  4846.  
  4847.     for(i = 0; i < ab->er_hashsize; i++){
  4848.     ab->head_cache_elem[i] = (ER_CACHE_ELEM_S *)NULL;
  4849.     ab->tail_cache_elem[i] = (ER_CACHE_ELEM_S *)NULL;
  4850.     }
  4851.  
  4852.     deleted_elem     = NO_NEXT;
  4853.     deleted_ae       = NULL;
  4854.     moved_elem       = NO_NEXT;
  4855.     move_elem_before = NO_NEXT;
  4856.     if(insert_before != NO_NEXT){
  4857.     insert_before    = NO_NEXT;
  4858.     if(inserted_entryref.ae != (AdrBk_Entry *)NULL){
  4859.         free_ae(ab, &(inserted_entryref.ae));
  4860.         inserted_entryref.ae = (AdrBk_Entry *)NULL;
  4861.     }
  4862.     }
  4863. }
  4864.  
  4865.  
  4866. /*
  4867.  * Clear the entire cache.
  4868.  */
  4869. void
  4870. clear_entryref_cache(ab)
  4871.     AdrBk *ab;
  4872. {
  4873.     ER_CACHE_ELEM_S *cptr, *next;
  4874.     adrbk_cntr_t i;
  4875.  
  4876.     dprint(9, (debugfile, "- clear_entryref_cache -\n"));
  4877.  
  4878.     for(i = 0; i < ab->er_hashsize; i++){
  4879.     ab->n_ae_cached_in_this_bucket[i] = 0;
  4880.     cptr = ab->head_cache_elem[i];
  4881.     while(cptr){
  4882.         free_ab_entryref(ab, cptr->entry);
  4883.         next = cptr->next;
  4884.         fs_give((void **)&cptr);
  4885.         cptr = next;
  4886.     }
  4887.     }
  4888. }
  4889.  
  4890.  
  4891. void
  4892. clearrefs_in_cached_aes(ab)
  4893.     AdrBk *ab;
  4894. {
  4895.     ER_CACHE_ELEM_S *cptr;
  4896.     adrbk_cntr_t i;
  4897.  
  4898.     for(i = 0; i < ab->er_hashsize; i++){
  4899.     cptr = ab->head_cache_elem[i];
  4900.     while(cptr){
  4901.         if(cptr->entry && cptr->entry->ae)
  4902.           cptr->entry->ae->referenced = 0;
  4903.  
  4904.         cptr = cptr->next;
  4905.     }
  4906.     }
  4907. }
  4908.  
  4909.  
  4910. /*
  4911.  * Free the list of distribution lists which have been expanded.
  4912.  * Leaves the head of the list alone.
  4913.  *
  4914.  * Args:  exp_head -- Head of the expanded list.
  4915.  */
  4916. void
  4917. exp_free(exp_head)
  4918.     EXPANDED_S *exp_head;
  4919. {
  4920.     EXPANDED_S *e, *the_next_one;
  4921.  
  4922.     e = exp_head ? exp_head->next : NULL;
  4923.  
  4924.     if(!e)
  4925.       return;
  4926.  
  4927.     while(e){
  4928.     the_next_one = e->next;
  4929.     fs_give((void **)&e);
  4930.     e = the_next_one;
  4931.     }
  4932.  
  4933.     exp_head->next = (EXPANDED_S *)NULL;
  4934. }
  4935.  
  4936.  
  4937. /*
  4938.  * Is entry n expanded?
  4939.  *
  4940.  * Args:  exp_head -- Head of the expanded list.
  4941.  *        n        -- The entry num to check
  4942.  */
  4943. int
  4944. exp_is_expanded(exp_head, n)
  4945.     EXPANDED_S *exp_head;
  4946.     a_c_arg_t   n;
  4947. {
  4948.     register EXPANDED_S *e;
  4949.     adrbk_cntr_t nn;
  4950.  
  4951.     nn = (adrbk_cntr_t)n;
  4952.  
  4953.     /*
  4954.      * The list is kept ordered, so we search until we find it or are
  4955.      * past it.
  4956.      */
  4957.     for(e = exp_head->next; e; e = e->next)
  4958.       if(e->ent >= nn)
  4959.     break;
  4960.     
  4961.     return(e && e->ent == nn);
  4962. }
  4963.  
  4964.  
  4965. /*
  4966.  * Return next entry num in list.
  4967.  *
  4968.  * Args:  cur -- Current position in the list.
  4969.  *
  4970.  * Result: Returns the number of the next entry, or NO_NEXT if there is
  4971.  *       no next entry.  As a side effect, the cur pointer is incremented.
  4972.  */
  4973. adrbk_cntr_t
  4974. exp_get_next(cur)
  4975.     EXPANDED_S **cur;
  4976. {
  4977.     adrbk_cntr_t ret = NO_NEXT;
  4978.  
  4979.     if(cur && *cur && (*cur)->next){
  4980.     ret  = (*cur)->next->ent;
  4981.     *cur = (*cur)->next;
  4982.     }
  4983.  
  4984.     return(ret);
  4985. }
  4986.  
  4987.  
  4988. /*
  4989.  * Mark entry n as being expanded.
  4990.  *
  4991.  * Args:  exp_head -- Head of the expanded list.
  4992.  *        n        -- The entry num to mark
  4993.  */
  4994. void
  4995. exp_set_expanded(exp_head, n)
  4996.     EXPANDED_S *exp_head;
  4997.     a_c_arg_t   n;
  4998. {
  4999.     register EXPANDED_S *e;
  5000.     EXPANDED_S *new;
  5001.     adrbk_cntr_t nn;
  5002.  
  5003.     nn = (adrbk_cntr_t)n;
  5004.  
  5005.     for(e = exp_head; e->next; e = e->next)
  5006.       if(e->next->ent >= nn)
  5007.     break;
  5008.     
  5009.     if(e->next && e->next->ent == nn) /* already there */
  5010.       return;
  5011.  
  5012.     /* add new after e */
  5013.     new       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  5014.     new->ent  = nn;
  5015.     new->next = e->next;
  5016.     e->next   = new;
  5017. }
  5018.  
  5019.  
  5020. /*
  5021.  * Mark entry n as being *not* expanded.
  5022.  *
  5023.  * Args:  exp_head -- Head of the expanded list.
  5024.  *        n        -- The entry num to mark
  5025.  */
  5026. void
  5027. exp_unset_expanded(exp_head, n)
  5028.     EXPANDED_S *exp_head;
  5029.     a_c_arg_t   n;
  5030. {
  5031.     register EXPANDED_S *e;
  5032.     EXPANDED_S *delete_this_one;
  5033.     adrbk_cntr_t nn;
  5034.  
  5035.     nn = (adrbk_cntr_t)n;
  5036.  
  5037.     for(e = exp_head; e->next; e = e->next)
  5038.       if(e->next->ent >= nn)
  5039.     break;
  5040.     
  5041.     if(e->next && e->next->ent == nn){
  5042.     delete_this_one = e->next;
  5043.     e->next = e->next->next;
  5044.     }
  5045.  
  5046.     fs_give((void **)&delete_this_one);
  5047. }
  5048.  
  5049.  
  5050. /*
  5051.  * Adjust the "expanded" list to correspond to addrbook entry n being
  5052.  * deleted.
  5053.  *
  5054.  * Args:  exp_head -- Head of the expanded list.
  5055.  *        n        -- The entry num being deleted
  5056.  */
  5057. void
  5058. exp_del_nth(exp_head, n)
  5059.     EXPANDED_S *exp_head;
  5060.     a_c_arg_t   n;
  5061. {
  5062.     register EXPANDED_S *e;
  5063.     int delete_when_done = 0;
  5064.     adrbk_cntr_t nn;
  5065.  
  5066.     nn = (adrbk_cntr_t)n;
  5067.  
  5068.     e = exp_head->next;
  5069.     while(e && e->ent < nn)
  5070.       e = e->next;
  5071.     
  5072.     if(e){
  5073.     if(e->ent == nn){
  5074.         delete_when_done++;
  5075.         e = e->next;
  5076.     }
  5077.  
  5078.     while(e){
  5079.         e->ent--; /* adjust entry nums */
  5080.         e = e->next;
  5081.     }
  5082.  
  5083.     if(delete_when_done)
  5084.       exp_unset_expanded(exp_head, n);
  5085.     }
  5086. }
  5087.  
  5088.  
  5089. /*
  5090.  * Adjust the "expanded" list to correspond to a new addrbook entry being
  5091.  * added between current entries n-1 and n.
  5092.  *
  5093.  * Args:  exp_head -- Head of the expanded list.
  5094.  *        n        -- The entry num being added
  5095.  *
  5096.  * The new entry is not marked expanded.
  5097.  */
  5098. void
  5099. exp_add_nth(exp_head, n)
  5100.     EXPANDED_S *exp_head;
  5101.     a_c_arg_t   n;
  5102. {
  5103.     register EXPANDED_S *e;
  5104.     adrbk_cntr_t nn;
  5105.  
  5106.     nn = (adrbk_cntr_t)n;
  5107.  
  5108.     e = exp_head->next;
  5109.     while(e && e->ent < nn)
  5110.       e = e->next;
  5111.     
  5112.     while(e){
  5113.     e->ent++; /* adjust entry nums */
  5114.     e = e->next;
  5115.     }
  5116. }
  5117.  
  5118.  
  5119. static AdrBk *ab_for_sort;
  5120.  
  5121. /*
  5122.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5123.  * Sorts lists after simple addresses and then sorts on Fullname field.
  5124.  */
  5125. int
  5126. cmp_ae_by_full_lists_last(a, b)
  5127.     const QSType *a,
  5128.          *b;
  5129. {
  5130.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5131.                 **y = (AdrBk_Entry **)b;
  5132.     int result;
  5133.  
  5134.     if((*x)->tag == List && (*y)->tag == Single)
  5135.       result = 1;
  5136.     else if((*x)->tag == Single && (*y)->tag == List)
  5137.       result = -1;
  5138.     else{
  5139.     register char *p, *q;
  5140.  
  5141.     p = (*x)->fullname;
  5142.     if(*p == '"' && *(p+1))
  5143.       p++;
  5144.  
  5145.     q = (*y)->fullname;
  5146.     if(*q == '"' && *(q+1))
  5147.       q++;
  5148.  
  5149.     result = strucmp(p, q);
  5150.     if(result == 0)
  5151.       result = strucmp((*x)->nickname, (*y)->nickname);
  5152.     }
  5153.       
  5154.     return(result);
  5155. }
  5156.  
  5157.  
  5158. /*
  5159.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5160.  * Sorts lists after simple addresses and then sorts on Fullname field.
  5161.  */
  5162. int
  5163. cmp_cntr_by_full_lists_last(a, b)
  5164.     const QSType *a,
  5165.          *b;
  5166. {
  5167.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5168.                  *y = (adrbk_cntr_t *)b;
  5169.     AdrBk_Entry  *x_ae,
  5170.          *y_ae;
  5171.  
  5172.     if(ps_global->intr_pending)
  5173.       longjmp(jump_over_qsort, 1);
  5174.  
  5175.     ALARM_BLIP();
  5176.  
  5177.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5178.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5179.  
  5180.     return(cmp_ae_by_full_lists_last((const QSType *) &x_ae,
  5181.                      (const QSType *) &y_ae));
  5182. }
  5183.  
  5184.  
  5185. /*
  5186.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5187.  * Sorts on Fullname field.
  5188.  */
  5189. int
  5190. cmp_ae_by_full(a, b)
  5191.     const QSType *a,
  5192.          *b;
  5193. {
  5194.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5195.                 **y = (AdrBk_Entry **)b;
  5196.     int result;
  5197.  
  5198.     result = strucmp((*x)->fullname, (*y)->fullname);
  5199.     if(result == 0)
  5200.       result = strucmp((*x)->nickname, (*y)->nickname);
  5201.       
  5202.     return(result);
  5203. }
  5204.  
  5205.  
  5206. /*
  5207.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5208.  * Sorts on Fullname field.
  5209.  */
  5210. int
  5211. cmp_cntr_by_full(a, b)
  5212.     const QSType *a,
  5213.          *b;
  5214. {
  5215.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5216.                  *y = (adrbk_cntr_t *)b;
  5217.     AdrBk_Entry  *x_ae,
  5218.          *y_ae;
  5219.  
  5220.     if(ps_global->intr_pending)
  5221.       longjmp(jump_over_qsort, 1);
  5222.  
  5223.     ALARM_BLIP();
  5224.  
  5225.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5226.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5227.  
  5228.     return(cmp_ae_by_full((const QSType *) &x_ae, (const QSType *) &y_ae));
  5229. }
  5230.  
  5231.  
  5232. /*
  5233.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5234.  * Sorts lists after simple addresses and then sorts on Nickname field.
  5235.  */
  5236. int
  5237. cmp_ae_by_nick_lists_last(a, b)
  5238.     const QSType *a,
  5239.          *b;
  5240. {
  5241.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5242.                 **y = (AdrBk_Entry **)b;
  5243.     int result;
  5244.  
  5245.     if((*x)->tag == List && (*y)->tag == Single)
  5246.       result = 1;
  5247.     else if((*x)->tag == Single && (*y)->tag == List)
  5248.       result = -1;
  5249.     else
  5250.       result = strucmp((*x)->nickname, (*y)->nickname);
  5251.  
  5252.     return(result);
  5253. }
  5254.  
  5255.  
  5256. /*
  5257.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5258.  * Sorts lists after simple addresses and then sorts on Nickname field.
  5259.  */
  5260. int
  5261. cmp_cntr_by_nick_lists_last(a, b)
  5262.     const QSType *a,
  5263.          *b;
  5264. {
  5265.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5266.                  *y = (adrbk_cntr_t *)b;
  5267.     AdrBk_Entry  *x_ae,
  5268.          *y_ae;
  5269.  
  5270.     if(ps_global->intr_pending)
  5271.       longjmp(jump_over_qsort, 1);
  5272.  
  5273.     ALARM_BLIP();
  5274.  
  5275.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5276.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5277.  
  5278.     return(cmp_ae_by_nick_lists_last((const QSType *) &x_ae,
  5279.                      (const QSType *) &y_ae));
  5280. }
  5281.  
  5282.  
  5283. /*
  5284.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5285.  * Sorts on Nickname field.
  5286.  */
  5287. int
  5288. cmp_ae_by_nick(a, b)
  5289.     const QSType *a,
  5290.          *b;
  5291. {
  5292.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5293.                 **y = (AdrBk_Entry **)b;
  5294.  
  5295.     return(strucmp((*x)->nickname, (*y)->nickname));
  5296. }
  5297.  
  5298.  
  5299. /*
  5300.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5301.  * Sorts on Nickname field.
  5302.  */
  5303. int
  5304. cmp_cntr_by_nick(a, b)
  5305.     const QSType *a,
  5306.          *b;
  5307. {
  5308.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5309.                  *y = (adrbk_cntr_t *)b;
  5310.     AdrBk_Entry  *x_ae,
  5311.          *y_ae;
  5312.  
  5313.     if(ps_global->intr_pending)
  5314.       longjmp(jump_over_qsort, 1);
  5315.  
  5316.     ALARM_BLIP();
  5317.  
  5318.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5319.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5320.  
  5321.     return(cmp_ae_by_nick((const QSType *) &x_ae, (const QSType *) &y_ae));
  5322. }
  5323.  
  5324.  
  5325. /*
  5326.  * For sorting a simple list of pointers to addresses (skip initial quotes)
  5327.  */
  5328. int
  5329. cmp_addr(a1, a2)
  5330.     const QSType *a1, *a2;
  5331. {
  5332.     char *x = *(char **)a1, *y = *(char **)a2;
  5333.  
  5334.     if(x && *x == '"')
  5335.       x++;
  5336.  
  5337.     if(y && *y == '"')
  5338.       y++;
  5339.  
  5340.     return(strucmp(x, y));
  5341. }
  5342.  
  5343.  
  5344. /*
  5345.  * Sort an array of strings, except skip initial quotes.
  5346.  */
  5347. void
  5348. sort_addr_list(list)
  5349.     char **list;
  5350. {
  5351.     register char **p;
  5352.  
  5353.     /* find size of list */
  5354.     for(p = list; *p != NULL; p++)
  5355.       ;/* do nothing */
  5356.  
  5357.     qsort((QSType *)list,
  5358. #ifdef DYN
  5359.           (p - list),
  5360. #else          
  5361.           (size_t)(p - list),
  5362. #endif          
  5363.           sizeof(char *), cmp_addr);
  5364. }
  5365.  
  5366.  
  5367. /*
  5368.  * Sort this address book.
  5369.  *
  5370.  * Args: ab            -- address book to sort
  5371.  *   current_entry_num -- see next description
  5372.  *   new_entry_num     -- return new entry_num of current_entry_num here
  5373.  *
  5374.  * Result: return code:  0 all went well
  5375.  *                      -2 error writing address book, check errno
  5376.  *
  5377.  * The sorting strategy is to allocate an array of length ab->count which
  5378.  * contains the element numbers 0, 1, ..., ab->count - 1, representing the
  5379.  * entries in the addrbook, of course.  Sort the array, then write it out in
  5380.  * the new order and start over from there.
  5381.  */
  5382. int
  5383. adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet)
  5384.     AdrBk        *ab;
  5385.     a_c_arg_t     current_entry_num;
  5386.     adrbk_cntr_t *new_entry_num;
  5387.     int           be_quiet;
  5388. {
  5389.     adrbk_cntr_t *sort_array;
  5390.     long i;
  5391.     long count;
  5392.     int result;
  5393.     int skip_the_sort = 0;
  5394.     int we_cancel = 0;
  5395.  
  5396.     dprint(7, (debugfile, "- adrbk_sort -\n"));
  5397.  
  5398.     count = (long)(ab->count);
  5399.  
  5400.     if(!ab)
  5401.       return -2;
  5402.  
  5403.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  5404.       return 0;
  5405.     
  5406.     if(count < 2)
  5407.       return 0;
  5408.  
  5409.     if(!be_quiet)
  5410.       we_cancel = busy_alarm(1, "Sorting address book", NULL, 1);
  5411.  
  5412.     sort_array = (adrbk_cntr_t *)fs_get((size_t)count * sizeof(adrbk_cntr_t));
  5413.     
  5414.     for(i = 0L; i < count; i++)
  5415.       sort_array[i] = (adrbk_cntr_t)i;
  5416.     
  5417.     ab_for_sort = ab;
  5418.  
  5419.  
  5420.     if(setjmp(jump_over_qsort))
  5421.       skip_the_sort = 1;
  5422.  
  5423.     if(!skip_the_sort){
  5424.     intr_handling_on();
  5425.     qsort((QSType *)sort_array,
  5426.         (size_t)count,
  5427.         sizeof(adrbk_cntr_t),
  5428.         (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  5429.                         cmp_cntr_by_full_lists_last :
  5430.         (ab->sort_rule == AB_SORT_RULE_FULL) ?
  5431.                         cmp_cntr_by_full :
  5432.         (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  5433.                         cmp_cntr_by_nick_lists_last :
  5434.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  5435.                         cmp_cntr_by_nick);
  5436.     }
  5437.  
  5438.     intr_handling_off();
  5439.     if(we_cancel)
  5440.       cancel_busy_alarm(0);
  5441.  
  5442.     if(skip_the_sort){
  5443.     q_status_message(SM_ORDER, 3, 3,
  5444.         "Address book sort cancelled, using old order for now");
  5445.     goto skip_the_write_too;
  5446.     }
  5447.  
  5448.     dprint(2,
  5449.         (debugfile, "- adrbk_sort (%s)\n",
  5450.       ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
  5451.        ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
  5452.         ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
  5453.          ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
  5454.  
  5455.     result = adrbk_write(ab, sort_array, be_quiet);
  5456.  
  5457.     if(result == 0)
  5458.       exp_free(ab->exp);
  5459.     else if(result == -2)
  5460.       q_status_message(SM_ORDER, 3, 4, "address book sort failed, can't save");
  5461.  
  5462.     /*
  5463.      * Look through the sort_array to find where current_entry_num moved to.
  5464.      */
  5465.     if(result == 0 && new_entry_num){
  5466.     for(i = 0L; i < count; i++)
  5467.       if((adrbk_cntr_t)current_entry_num == sort_array[i]){
  5468.           *new_entry_num = (adrbk_cntr_t)i;
  5469.           break;
  5470.       }
  5471.     }
  5472.  
  5473. skip_the_write_too:
  5474.     fs_give((void **)&sort_array);
  5475.  
  5476.     return(result);
  5477. }
  5478.